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 {
  createDesignationDto,
  designationResponseSchema,
  designationsResponseSchema,
  updateDesignationDto,
} from './designations.dto';

@Injectable()
export class DesignationsService {
  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 createDesignation(
    data: createDesignationDto,
    owner_id: string,
    ctx: PermissionUserContext,
  ) {
    try {
      await this.assertGeneralAccess(ctx, true);
      const existing = await this.db.designations.findFirst({
        where: {
          designation_code: data.designation_code,
          owner_id,
        },
      });

      if (existing) {
        throw createGeneralError(
          'Designation with same name or code already exists',
          'designation_code',
          HttpStatus.CONFLICT,
        );
      }

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

      const { data: parsed } = designationResponseSchema.safeParse(designation);

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

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

      const { data: parsed } =
        designationsResponseSchema.safeParse(designations);

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

  async getFilteredDesignations(
    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 } },
          { designation_code: { contains: query.search } },
        ];
      }

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

      const { data: parsed } =
        designationsResponseSchema.safeParse(designations);

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

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

  async getDesignationById(
    id: string,
    owner_id: string,
    ctx: PermissionUserContext,
  ) {
    try {
      await this.assertGeneralAccess(ctx);
      const designation = await this.db.designations.findUnique({
        where: { id, owner_id },
      });

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

      const { data: parsed } = designationResponseSchema.safeParse(designation);

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

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

      if (!existing) {
        throw createGeneralError(
          'Designation not found',
          'designation',
          HttpStatus.NOT_FOUND,
        );
      }

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

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

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

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

      if (!existing) {
        throw createGeneralError(
          'Designation not found',
          'designation',
          HttpStatus.NOT_FOUND,
        );
      }

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

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

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

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

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

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