import { HttpStatus, Injectable } from '@nestjs/common';
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 {
  createEmployementTypeDto,
  employementTypeResponseSchema,
  employementTypesResponseSchema,
  updateEmployementTypeDto,
} from './employement_types.dto';

@Injectable()
export class EmployementTypesService {
  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 createEmployementType(
    data: createEmployementTypeDto,
    owner_id: string,
    ctx: PermissionUserContext,
  ) {
    try {
      await this.assertGeneralAccess(ctx, true);
      const existing = await this.db.employement_types.findFirst({
        where: { name: data.name, owner_id },
      });

      if (existing) {
        throw createGeneralError(
          'Employement type with same name already exists',
          'name',
          HttpStatus.CONFLICT,
        );
      }

      const employementType = await this.db.employement_types.create({
        data: {
          name: data.name,
          ...(data.description ? { description: data.description } : {}),
          ...(data.status ? { status: data.status } : {}),
          owner_id,
        },
      });

      const { data: parsed } =
        employementTypeResponseSchema.safeParse(employementType);

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

  async getEmployementTypes(owner_id: string, ctx: PermissionUserContext) {
    try {
      await this.assertGeneralAccess(ctx);
      const employementTypes = await this.db.employement_types.findMany({
        where: {
          owner_id,
          status: {
            not: 'deleted',
          },
        },
        orderBy: { created_at: 'asc' },
      });

      const { data: parsed } =
        employementTypesResponseSchema.safeParse(employementTypes);

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

  async getFilteredEmployementTypes(
    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 where: any = {
        owner_id,
        status: {
          not: 'deleted',
        },
      };

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

      const [employementTypes, total] = await Promise.all([
        this.db.employement_types.findMany({
          where,
          orderBy: { created_at: 'asc' },
          skip,
          take: limit,
        }),
        this.db.employement_types.count({ where }),
      ]);

      const { data: parsed } =
        employementTypesResponseSchema.safeParse(employementTypes);

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

      return {
        data: parsed,
        message: 'Employement types 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 getFilteredEmployementTypes: ${String(error)}`);
      if (error instanceof ErrorResponse) throw error;
      throw createGeneralError(String(error), 'root', HttpStatus.BAD_REQUEST);
    }
  }

  async getEmployementTypeById(
    id: string,
    owner_id: string,
    ctx: PermissionUserContext,
  ) {
    try {
      await this.assertGeneralAccess(ctx);
      const employementType = await this.db.employement_types.findUnique({
        where: { id, owner_id },
      });

      if (!employementType) {
        throw createGeneralError(
          'Employement type not found',
          'employement_type',
          HttpStatus.NOT_FOUND,
        );
      }

      const { data: parsed } =
        employementTypeResponseSchema.safeParse(employementType);

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

  async updateEmployementType(
    id: string,
    data: updateEmployementTypeDto,
    owner_id: string,
    ctx: PermissionUserContext,
  ) {
    try {
      await this.assertGeneralAccess(ctx, true);
      const existing = await this.db.employement_types.findUnique({
        where: { id, owner_id },
      });

      if (!existing) {
        throw createGeneralError(
          'Employement type not found',
          'employement_type',
          HttpStatus.NOT_FOUND,
        );
      }

      const updated = await this.db.employement_types.update({
        where: { id, owner_id },
        data,
      });

      const { data: parsed } = employementTypeResponseSchema.safeParse(updated);

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

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

      if (!existing) {
        throw createGeneralError(
          'Employement type not found',
          'employement_type',
          HttpStatus.NOT_FOUND,
        );
      }

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

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

        const { data: parsed } =
          employementTypeResponseSchema.safeParse(softDeleted);

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

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

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