import { HttpStatus, Injectable } from '@nestjs/common';
import { Not } from 'typeorm';
import { assertGeneralModuleAccess } from '../../common/access-control/permission-resolver.helpers';
import { PermissionUserContext } from '../../common/access-control/permission-context.types';
import { PermissionResolverService } from '../../common/access-control/permission-resolver.service';
import { DatabaseService, ErrorResponse, logger } from '../../config';
import { createGeneralError } from '../../utils';
import { defaultPaginationDto } from '../shared/shared.dto';
import {
  createDepartmentDto,
  departmentResponseSchema,
  departmentsResponseSchema,
  updateDepartmentDto,
} from './departments.dto';

@Injectable()
export class DepartmentsService {
  constructor(
    private readonly db: DatabaseService,
    private readonly permissionResolver: PermissionResolverService,
  ) {}

  private async assertGeneralAccess(
    ctx: PermissionUserContext,
    manage = false,
  ) {
    const permissions = await this.permissionResolver.resolveForUser(ctx);
    assertGeneralModuleAccess(permissions, manage);
  }

  async createDepartment(
    data: createDepartmentDto,
    owner_id: string,
    ctx: PermissionUserContext,
  ) {
    try {
      await this.assertGeneralAccess(ctx, true);
      const existingDepartment = await this.db.departments.findFirst({
        where: {
          department_code: data.department_code,
          owner_id,
          status: Not('deleted'), // ✅ Fixed from NOT: { status: 'deleted' }
        },
      });

      if (existingDepartment) {
        throw createGeneralError(
          'Department with same name or code already exists',
          'department_code',
          HttpStatus.CONFLICT,
        );
      }

      const deletedDepartment = await this.db.departments.findFirst({
        where: {
          department_code: data.department_code,
          owner_id,
          status: 'deleted',
        },
      });

      if (deletedDepartment) {
        const restoredDepartment = await this.db.departments.update({
          where: { id: deletedDepartment.id },
          data: {
            name: data.name,
            department_code: data.department_code,
            description: data.description ?? 'NT/APL',
            status: data.status ?? 'active',
            department_head_id: data.department_head_id ?? null,
          },
        });

        const { data: parsedDepartment } =
          departmentResponseSchema.safeParse(restoredDepartment);

        return {
          data: parsedDepartment,
          message: 'Department created successfully',
          status: HttpStatus.CREATED,
          details: null,
        };
      }

      const department = await this.db.departments.create({
        data: {
          name: data.name,
          department_code: data.department_code,
          ...(data.description ? { description: data.description } : {}),
          ...(data.status ? { status: data.status } : {}),
          ...(data.department_head_id
            ? { department_head_id: data.department_head_id }
            : {}),
          owner_id,
        },
      });

      const { data: parsedDepartment } =
        departmentResponseSchema.safeParse(department);

      return {
        data: parsedDepartment,
        message: 'Department created successfully',
        status: HttpStatus.CREATED,
        details: null,
      };
    } catch (error) {
      logger.error(`Error in createDepartment: ${String(error)}`);
      if (error instanceof ErrorResponse) {
        throw error;
      }
      throw createGeneralError(String(error), 'root', HttpStatus.BAD_REQUEST);
    }
  }

  async getDepartments(owner_id: string, ctx: PermissionUserContext) {
    try {
      await this.assertGeneralAccess(ctx);
      const departments = await this.db.departments.findMany({
        orderBy: { created_at: 'asc' },
        where: {
          owner_id,
          status: Not('deleted'), // ✅ Fixed
        },
        include: {
          _count: {
            select: {
              members: true,
            },
          },
        },
      });

      const departmentList = departments.map(
        ({ _count, ...department }: any) => ({
          ...department,
          members_count: _count?.members ?? 0, // ✅ null safety
        }),
      );

      const { data: parsedDepartments } =
        departmentsResponseSchema.safeParse(departmentList);

      return {
        data: parsedDepartments,
        message: 'Departments retrieved successfully',
        status: HttpStatus.OK,
        details: null,
      };
    } catch (error) {
      logger.error(`Error in getDepartments: ${String(error)}`);
      if (error instanceof ErrorResponse) {
        throw error;
      }
      throw createGeneralError(String(error), 'root', HttpStatus.BAD_REQUEST);
    }
  }

  async getFilteredDepartments(
    owner_id: string,
    query: defaultPaginationDto,
    ctx: PermissionUserContext,
  ) {
    try {
      await this.assertGeneralAccess(ctx);
      const page = query.page ?? 1;
      const limit = query.limit ?? 10;
      const skip = (page - 1) * limit;

      const baseWhere: any = {
        owner_id,
        status: Not('deleted'), // ✅ Fixed
      };

      if (query.search) {
        baseWhere.OR = [
          { name: { contains: query.search } },
          { department_code: { contains: query.search } },
        ];
      }

      const [departments, total] = await Promise.all([
        this.db.departments.findMany({
          where: baseWhere,
          include: {
            department_head: true,
            _count: {
              select: {
                members: true,
              },
            },
          },
          orderBy: { created_at: 'asc' },
          skip,
          take: limit,
        }),
        this.db.departments.count({ where: baseWhere }),
      ]);

      const departmentList = departments.map(
        ({ _count, ...department }: any) => ({
          ...department,
          members_count: _count?.members ?? 0, // ✅ null safety
        }),
      );

      const { data: parsedDepartments, error } =
        departmentsResponseSchema.safeParse(departmentList);

      const totalPages = Math.ceil(total / limit);

      return {
        data: parsedDepartments,
        message: 'Departments retrieved successfully',
        status: HttpStatus.OK,
        details: {
          pagination: {
            total_items: total,
            total_pages: totalPages,
            current_page: page,
            items_per_page: limit,
            has_next_page: page < totalPages,
            has_previous_page: page > 1,
            next_page: page < totalPages ? page + 1 : null,
            previous_page: page > 1 ? page - 1 : null,
          },
        },
      };
    } catch (error) {
      logger.error(`Error in getFilteredDepartments: ${String(error)}`);
      if (error instanceof ErrorResponse) {
        throw error;
      }
      throw createGeneralError(String(error), 'root', HttpStatus.BAD_REQUEST);
    }
  }

  async getDepartmentById(
    id: string,
    owner_id: string,
    ctx: PermissionUserContext,
  ) {
    try {
      await this.assertGeneralAccess(ctx);
      const department = await this.db.departments.findUnique({
        where: { id, owner_id },
        include: {
          department_head: true, // ✅ Uncommented, works with TypeORM
        },
      });

      if (!department) {
        throw createGeneralError(
          'Department not found',
          'department',
          HttpStatus.NOT_FOUND,
        );
      }

      const parsedDepartment = {
        ...department,
        department_head: department.department_head
          ? {
              employee_id: department.department_head.employee_id,
              nickname: department.department_head.nickname,
              first_name: department.department_head.first_name,
              last_name: department.department_head.last_name,
              image: department.department_head.image,
              role: department.department_head.role,
            }
          : null,
      };

      const { data: validatedDepartment } =
        departmentResponseSchema.safeParse(parsedDepartment);

      return {
        data: validatedDepartment,
        message: 'Department retrieved successfully',
        status: HttpStatus.OK,
        details: null,
      };
    } catch (error) {
      logger.error(`Error in getDepartmentById: ${String(error)}`);
      if (error instanceof ErrorResponse) {
        throw error;
      }
      throw createGeneralError(String(error), 'root', HttpStatus.BAD_REQUEST);
    }
  }

  async updateDepartment(
    id: string,
    data: updateDepartmentDto,
    owner_id: string,
    ctx: PermissionUserContext,
  ) {
    try {
      await this.assertGeneralAccess(ctx, true);
      const existingDepartment = await this.db.departments.findUnique({
        where: { id, owner_id },
      });

      if (!existingDepartment) {
        throw createGeneralError(
          'Department not found',
          'department',
          HttpStatus.NOT_FOUND,
        );
      }

      const { department_head_id, ...rest } = data;

      const updatedDepartment = await this.db.departments.update({
        where: { id, owner_id },
        data: {
          ...rest,
          // ✅ Fixed: use department_head_id directly instead of Prisma-style connect
          ...(typeof department_head_id === 'string'
            ? { department_head_id }
            : department_head_id === null
              ? { department_head_id: null }
              : {}),
        },
        include: {
          department_head: true,
          members: true,
        },
      });

      const { data: parsedDepartment } =
        departmentResponseSchema.safeParse(updatedDepartment);

      return {
        data: parsedDepartment,
        message: 'Department updated successfully',
        status: HttpStatus.OK,
        details: null,
      };
    } catch (error) {
      logger.error(`Error in updateDepartment: ${String(error)}`);
      if (error instanceof ErrorResponse) {
        throw error;
      }
      throw createGeneralError(String(error), 'root', HttpStatus.BAD_REQUEST);
    }
  }

  async deleteDepartment(
    id: string,
    owner_id: string,
    ctx: PermissionUserContext,
  ) {
    try {
      await this.assertGeneralAccess(ctx, true);
      const existingDepartment = await this.db.departments.findUnique({
        where: { id, owner_id },
      });

      if (!existingDepartment) {
        throw createGeneralError(
          'Department not found',
          'department',
          HttpStatus.NOT_FOUND,
        );
      }

      const usageCount = await this.db.members.count({
        where: { department_id: id, owner_id },
      });

      if (usageCount > 0) {
        await this.db.departments.update({
          where: { id, owner_id },
          data: { status: 'deleted' },
        });

        return {
          data: null,
          message: 'Department is in use. Status changed to deleted.',
          status: HttpStatus.OK,
          details: {
            usedByMembers: usageCount,
          },
        };
      }

      await this.db.departments.delete({
        where: { id, owner_id },
      });

      return {
        data: null,
        message: 'Department deleted successfully',
        status: HttpStatus.OK,
        details: null,
      };
    } catch (error) {
      logger.error(`Error in deleteDepartment: ${String(error)}`);
      if (error instanceof ErrorResponse) {
        throw error;
      }
      throw createGeneralError(String(error), 'root', HttpStatus.BAD_REQUEST);
    }
  }
}
