import { Request, Response } from 'express';

import * as unitService from '@/modules/project/unit/unit.service';
import catchAsync from '@/shared/utils/catchAsync';
import responseCodes from '@/shared/utils/responseCode/responseCode';
import { pick } from '@/shared/utils';
import { getObjectId } from '@/shared/utils/commonHelper';
import { defaultStatus } from '@/shared/utils/responseCode/httpStatusAlias';
import { ApiError } from '@/shared/utils/errors';
import Project from '@/modules/project/project.model';

const { ProjectResponseCodes } = responseCodes;

export const createUnit = catchAsync(async (req: Request, res: Response) => {
  const { id: userId } = req.user;
  const { projectId } = req.params;

  // Resolve project and enforce conditional validation based on hasBlockLayout
  const project = await Project.findById(projectId)
    .select('hasBlockLayout')
    .lean();
  if (!project)
    throw new ApiError(
      defaultStatus.BAD_REQUEST,
      'Invalid projectId',
      true,
      '',
      ProjectResponseCodes.PROJECT_ERROR,
    );

  const body = { ...req.body } as Record<string, unknown>;

  if (project.hasBlockLayout) {
    const block = body.block as string | null | undefined;
    const floor = body.floor as number | null | undefined;
    if (!block || typeof block !== 'string' || block.trim().length === 0)
      throw new ApiError(
        defaultStatus.BAD_REQUEST,
        'block is required when project has block layout',
        true,
        '',
        ProjectResponseCodes.PROJECT_ERROR,
      );
    if (typeof floor !== 'number')
      throw new ApiError(
        defaultStatus.BAD_REQUEST,
        'floor is required and must be a number when project has block layout',
        true,
        '',
        ProjectResponseCodes.PROJECT_ERROR,
      );
  } else {
    // Clear block and floor if provided
    body.block = null;
    body.floor = null;
  }

  const unitPayload = {
    ...body,
    project: projectId,
    createdBy: userId,
  };

  const unit = await unitService.createUnit(unitPayload);

  return res.success(
    unit,
    ProjectResponseCodes.SUCCESS,
    'Unit Created Successfully',
  );
});

export const updateUnit = catchAsync(async (req: Request, res: Response) => {
  const { id, projectId } = pick(req.params, ['id', 'projectId']);
  const validId = getObjectId(id);
  // Resolve project and enforce conditional validation based on hasBlockLayout
  const project = await Project.findById(projectId)
    .select('hasBlockLayout')
    .lean();
  if (!project)
    throw new ApiError(
      defaultStatus.BAD_REQUEST,
      'Invalid projectId',
      true,
      '',
      ProjectResponseCodes.PROJECT_ERROR,
    );

  const body = { ...req.body } as Record<string, unknown>;

  if (project.hasBlockLayout) {
    if ('block' in body) {
      const block = body.block as string | null | undefined;
      if (!block || typeof block !== 'string' || block.trim().length === 0)
        throw new ApiError(
          defaultStatus.BAD_REQUEST,
          'block is required when project has block layout',
          true,
          '',
          ProjectResponseCodes.PROJECT_ERROR,
        );
    }
    if ('floor' in body) {
      const floor = body.floor as number | null | undefined;
      if (typeof floor !== 'number')
        throw new ApiError(
          defaultStatus.BAD_REQUEST,
          'floor is required and must be a number when project has block layout',
          true,
          '',
          ProjectResponseCodes.PROJECT_ERROR,
        );
    }
  } else {
    body.block = null;
    body.floor = null;
  }

  const updatedUnit = await unitService.updateUnit(validId, body as any);
  res.success(
    updatedUnit,
    ProjectResponseCodes.SUCCESS,
    'Unit Updated Successfully',
  );
});

export const getUnitById = catchAsync(async (req: Request, res: Response) => {
  const { id } = pick(req.params, ['id']);

  const unit = await unitService.getUnitById(id);

  res.success(unit, ProjectResponseCodes.SUCCESS, 'Unit Fetched Successfully');
});

export const getUnits = catchAsync(async (req: Request, res: Response) => {
  const filter = pick(req.query, [
    'propertyType',
    'status',
    'createdBy',
    'projectId',
    'companyId',
  ]);
  const options = pick(req.query, [
    'sortBy',
    'limit',
    'page',
    'populate',
    'includeTimeStamps',
    'fields',
  ]);

  const units = await unitService.queryUnits(filter, options);

  res.success(
    units,
    ProjectResponseCodes.SUCCESS,
    'Units Fetched Successfully',
  );
});

export const deleteUnit = catchAsync(async (req: Request, res: Response) => {
  const { id } = pick(req.params, ['id']);

  await unitService.deleteUnit(id);

  res.success(
    { id },
    ProjectResponseCodes.SUCCESS,
    'Unit Deleted Successfully',
  );
});

export const getUnitsByProjectId = catchAsync(
  async (req: Request, res: Response) => {
    const xClient = req.headers['x-client'];
    const { projectId } = req.params;
    const validId = getObjectId(projectId);
    const filter = pick(req.query, [
      'propertyType',
      'status',
      'statuses',
      'createdBy',
      'companyId',
      'search',
    ]);
    const options = pick(req.query, [
      'sortBy',
      'limit',
      'page',
      'populate',
      'includeTimeStamps',
      'fields',
    ]);

    let units;

    if (xClient === 'app')
      units = await unitService.getUnitsPerBlockAndFloor(projectId);
    else
      units = await unitService.getUnitsByProjectId(validId, options, filter);

    res.success(
      units,
      ProjectResponseCodes.SUCCESS,
      'Units Fetched Successfully',
    );
  },
);

export const getUnitsByProjectIdAnalytics = catchAsync(
  async (req: Request, res: Response) => {
    const { projectId } = req.params;
    const validId = getObjectId(projectId);

    const units = await unitService.getUnitsAnalytics(validId);
    res.success(
      units,
      ProjectResponseCodes.SUCCESS,
      'Units Fetched Successfully',
    );
  },
);

export const downloadUnitsExcel = catchAsync(
  async (req: Request, res: Response) => {
    const { projectId } = req.params as { projectId: string };
    const { company } = req.user;

    const project = await Project.findById(projectId)
      .select('hasBlockLayout projectName')
      .lean();
    if (!project)
      throw new ApiError(
        defaultStatus.BAD_REQUEST,
        'Invalid projectId',
        true,
        '',
        ProjectResponseCodes.PROJECT_ERROR,
      );

    const excelBuffer = await unitService.generateUnitsExcelTemplate(
      !!project.hasBlockLayout,
      company.id,
    );

    res.setHeader(
      'Content-Type',
      'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
    );
    res.setHeader(
      'Content-Disposition',
      `attachment; filename=units-template${project.hasBlockLayout ? '-block-floor' : ''}.xlsx`,
    );
    res.send(excelBuffer);
  },
);

export const uploadUnitsExcel = catchAsync(
  async (req: Request, res: Response) => {
    const { id: userId, company } = req.user;
    const { projectId } = req.params;
    const { propertyType } = req.body;

    if (!req.file)
      throw new ApiError(
        defaultStatus.BAD_REQUEST,
        'Failed to generate Excel template',
        true,
        '',
        ProjectResponseCodes.PROJECT_ERROR,
      );

    const fileName = req.file.originalname;
    const filePath = req.file.path;

    const jobId = await unitService.processBulkUpload(
      filePath,
      fileName,
      projectId,
      propertyType,
      userId,
      company.id,
    );

    res.success(
      { jobId },
      ProjectResponseCodes.SUCCESS,
      'Units upload job started successfully',
    );
  },
);
