import httpStatus from 'http-status';
import { Request, Response } from 'express';
import mongoose from 'mongoose';
import catchAsync from '@/shared/utils/catchAsync';
import ApiError from '@/shared/utils/errors/ApiError';
import pick from '@/shared/utils/pick';
import * as IndividualPropertiesService from './individualProperties.service';
import responseCodes from '@/shared/utils/responseCode/responseCode';
import { getObjectId } from '@/shared/utils/commonHelper';
import { findProspectLeadsForProperty } from './prospects/prospects.service';
import ExcelJS from 'exceljs';
import * as rulesService from '@/modules/rules/rules.service';
import { UserType } from '@/shared/constants/enum.constant';

const { IndividualPropertiesResponseCodes } = responseCodes;

export const createIndividualProperties = catchAsync(
  async (req: Request, res: Response) => {
    const property =
      await IndividualPropertiesService.createIndividualProperties({
        ...req.body,
        addedBy: `${req.user.firstName} ${req.user.lastName}`,
      });

    res.success(
      property,
      IndividualPropertiesResponseCodes.SUCCESS,
      'Property Created Successfully',
    );
  },
);

export const getIndividualPropertiesList = catchAsync(
  async (req: Request, res: Response) => {
    const filter = pick(req.query, [
      'title',
      'propertyType',
      'propertySubtype',
      'availableFor',
      'listingType',
      'city',
      'locality',
      'availability',
      'addedBy',
      'minPrice',
      'maxPrice',
      'pincode',
      'search',
      'status',
      'companyId',
      'project',
      'ageOfProperty',
      'furnishingType',
      'configuration',
    ]);
    const options = pick(req.query, [
      'sortBy',
      'limit',
      'page',
      'populate',
      'includeTimeStamps',
      'fields',
    ]);
    if (filter.addedBy !== undefined) {
      filter.createdBy = filter.addedBy;
      delete filter.addedBy;
    }
    const userId = req.user?.id ? new mongoose.Types.ObjectId(req.user.id) : undefined;
    const userType = req.user?.userType;
    const userTeams = req.user?.team?.map((t: string) => new mongoose.Types.ObjectId(t)) || [];

    const result = await IndividualPropertiesService.queryPrimaryProperties(
      filter,
      options,
      userId,
      userType,
      userTeams,
    );

    res.success(
      result,
      IndividualPropertiesResponseCodes.SUCCESS,
      'Property Fetched Successfully',
    );
  },
);

export const exportIndividualProperties = catchAsync(
  async (req: Request, res: Response) => {
    const filter = pick(req.query, [
      'title',
      'propertyType',
      'propertySubtype',
      'availableFor',
      'listingType',
      'city',
      'locality',
      'availability',
      'addedBy',
      'minPrice',
      'maxPrice',
      'pincode',
      'search',
      'status',
      'companyId',
      'project',
      'ageOfProperty',
      'furnishingType',
      'configuration',
      'ids',
    ]);
    if (filter.addedBy !== undefined) {
      filter.createdBy = filter.addedBy;
      delete filter.addedBy;
    }
    filter.companyId = req.user?.company?.id;
    const companyId = req.user?.company?.id;

    if (!companyId) {
      return res.status(400).json({
        message: 'Company context required for export',
        code: IndividualPropertiesResponseCodes.INDIVIDUAL_PROPERTY_ERROR,
      });
    }

    const rules = (await rulesService.getRules(
      companyId,
      'export_data',
    )) as Record<string, boolean>;
    const canExport =
      rules.export_data ||
      req.user.userType === UserType.ADMIN ||
      req.user.userType === UserType.SUPERADMIN;
    if (!canExport) {
      return res.status(403).json({
        message: 'Export not allowed',
        code: IndividualPropertiesResponseCodes.INDIVIDUAL_PROPERTY_ERROR,
      });
    }

    if (filter.ids) {
      const ids = Array.isArray(filter.ids) ? filter.ids : [filter.ids];
      const validIds = ids.filter((id) => getObjectId(id));
      if (validIds.length > 0) {
        filter._id = { $in: validIds.map((id) => new mongoose.Types.ObjectId(id)) };
      }
      delete filter.ids;
    }

    const options = {
      limit: 10000,
      page: 1,
      populate:
        'project:projectName;locality:name;city:name;propertyType:name;createdBy:firstName,lastName;configuration:name',
      fields:
        'id title listingType propertyType price monthlyRent carpetArea bedrooms locality city project status createdBy createdAt configuration',
    };
    const userId = req.user?.id ? new mongoose.Types.ObjectId(req.user.id) : undefined;
    const userType = req.user?.userType;
    const userTeams =
      req.user?.team?.map((t: string) => new mongoose.Types.ObjectId(t)) || [];

    const result = await IndividualPropertiesService.queryPrimaryProperties(
      filter,
      options,
      userId,
      userType,
      userTeams,
    );

    const properties = result.results ?? result.docs ?? [];

    const workbook = new ExcelJS.Workbook();
    const worksheet = workbook.addWorksheet('Properties');
    worksheet.columns = [
      { header: 'Prospects', key: 'prospects', width: 10 },
      { header: 'Listing Type', key: 'listingType', width: 18 },
      { header: 'Property', key: 'property', width: 40 },
      { header: 'Property Details', key: 'propertyDetails', width: 22 },
      { header: 'Project & Area', key: 'projectAndArea', width: 28 },
      { header: 'Price', key: 'price', width: 14 },
      { header: 'Added By', key: 'addedBy', width: 22 },
      { header: 'Added Date', key: 'addedDate', width: 14 },
    ];

    properties.forEach((prop: any) => {
      const propertyTypeName =
        prop.propertyType?.name || (typeof prop.propertyType === 'string' ? prop.propertyType : '') || '';
      const listingTypeLabel =
        prop.listingType === 'sell'
          ? 'Sell'
          : prop.listingType === 'rent'
            ? 'Rent'
            : prop.listingType === 'lease'
              ? 'Lease'
              : prop.listingType === 'preLease'
                ? 'Pre-Lease'
                : prop.listingType || '';
      const listingTypeCol = [propertyTypeName, listingTypeLabel].filter(Boolean).join(', ');
      const bhk = prop.bedrooms ? `${prop.bedrooms} BHK` : '';
      const area = prop.carpetArea ? `${prop.carpetArea} Sq Ft` : '';
      const propertyDetails = [bhk, area].filter(Boolean).join(' • ');
      const projectName = prop.project?.projectName || (typeof prop.project === 'object' && prop.project?.projectName) || '';
      const localityName = prop.locality?.name || (typeof prop.locality === 'object' && prop.locality?.name) || '';
      const projectAndArea = [projectName, localityName].filter(Boolean).join(' • ');
      const priceVal = prop.price ?? prop.monthlyRent;
      const priceStr =
        priceVal != null
          ? `₹${Number(priceVal) >= 10000000 ? `${(Number(priceVal) / 10000000).toFixed(2)} Cr` : `${(Number(priceVal) / 100000).toFixed(0)} L`}`
          : '';
      const addedByName = prop.createdBy
        ? `${prop.createdBy.firstName || ''} ${prop.createdBy.lastName || ''}`.trim()
        : '';
      const addedDate = prop.createdAt
        ? new Date(prop.createdAt).toLocaleDateString('en-IN', {
            day: '2-digit',
            month: 'short',
            year: 'numeric',
          })
        : '';

      worksheet.addRow({
        prospects: prop.prospectCount ?? 0,
        listingType: listingTypeCol,
        property: prop.title || '',
        propertyDetails,
        projectAndArea,
        price: priceStr,
        addedBy: addedByName,
        addedDate,
      });
    });

    res.setHeader(
      'Content-Type',
      'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
    );
    res.setHeader('Content-Disposition', 'attachment; filename=properties.xlsx');
    const buffer = await workbook.xlsx.writeBuffer();
    res.send(buffer);
  },
);

export const getIndividualPropertiesAppList = catchAsync(
  async (req: Request, res: Response) => {
    const filter = pick(req.query, [
      'title',
      'propertyType',
      'propertySubtype',
      'availableFor',
      'listingType',
      'city',
      'locality',
      'availability',
      'addedBy',
      'price',
      'pincode',
      'search',
      'status',
      'companyId',
      'furnishingType',
    ]);

    if (filter.propertySubtype !== undefined) {
      filter.subcategory = filter.propertySubtype;
      delete filter.propertySubtype;
    }
    if (filter.addedBy !== undefined) {
      filter.createdBy = filter.addedBy;
      delete filter.addedBy;
    }

    const options = pick(req.query, [
      'sortBy',
      'limit',
      'page',
      'populate',
      'includeTimeStamps',
      'fields',
    ]);
    const userId = req.user?.id ? new mongoose.Types.ObjectId(req.user.id) : undefined;
    const userType = req.user?.userType;
    const userTeams = req.user?.team?.map((t: string) => new mongoose.Types.ObjectId(t)) || [];

    const result = await IndividualPropertiesService.queryPrimaryPropertiesApp(
      filter,
      options,
      userId,
      userType,
      userTeams,
    );

    res.success(
      result,
      IndividualPropertiesResponseCodes.SUCCESS,
      'Property Fetched Successfully',
    );
  },
);

export const getIndividualProperties = catchAsync(
  async (req: Request, res: Response) => {
    const { propertyId } = req.params;
    if (!getObjectId(propertyId))
      throw new ApiError(httpStatus.BAD_REQUEST, 'Invalid ID');

    const userId = req.user?.id ? new mongoose.Types.ObjectId(req.user.id) : undefined;
    const userType = req.user?.userType;
    const userTeams = req.user?.team || [];

    const property =
      await IndividualPropertiesService.getIndividualPropertiesById(
        new mongoose.Types.ObjectId(propertyId),
        userId,
        userType,
        userTeams,
      );
    if (!property)
      throw new ApiError(httpStatus.NOT_FOUND, 'Primary property not found');

    res.success(
      property,
      IndividualPropertiesResponseCodes.SUCCESS,
      'Property Fetched Successfully',
    );
  },
);

export const updateIndividualProperties = catchAsync(
  async (req: Request, res: Response) => {
    if (typeof req.params.propertyId === 'string') {
      const property =
        await IndividualPropertiesService.updateIndividualPropertiesById(
          new mongoose.Types.ObjectId(req.params.propertyId),
          req.body,
        );
      res.success(
        property,
        IndividualPropertiesResponseCodes.SUCCESS,
        'Property Updated Successfully',
      );
    } else {
      throw new ApiError(httpStatus.BAD_REQUEST, 'Invalid property ID');
    }
  },
);

export const deleteIndividualProperties = catchAsync(
  async (req: Request, res: Response) => {
    const { propertyId } = req.params;
    if (!getObjectId(propertyId))
      throw new ApiError(httpStatus.BAD_REQUEST, 'Invalid ID');

    await IndividualPropertiesService.deleteIndividualPropertiesById(
      propertyId,
    );
    res.success(
      true,
      IndividualPropertiesResponseCodes.SUCCESS,
      'Property Deleted Successfully',
    );
  },
);

export const getProspects = catchAsync(async (req: Request, res: Response) => {
  const companyId = req.user?.company?.id;
  const result = await findProspectLeadsForProperty({
    propertyId: req.params.propertyId,
    companyId,
  });
  res.success(
    result,
    IndividualPropertiesResponseCodes.SUCCESS,
    'Property Fetched Successfully',
  );
});

export const getPropertyVisitors = catchAsync(async (req: Request, res: Response) => {
  const { propertyId } = req.params;
  const companyId = req.user?.company?.id;

  if (!getObjectId(propertyId as string)) {
    throw new ApiError(httpStatus.BAD_REQUEST, 'Invalid property ID');
  }

  if (!companyId) {
    throw new ApiError(httpStatus.BAD_REQUEST, 'Company ID is required');
  }

  const filter = pick(req.query, ['status']);
  const options = pick(req.query, ['page', 'limit']);

  const result = await IndividualPropertiesService.getPropertyVisitors(
    new mongoose.Types.ObjectId(propertyId as string),
    new mongoose.Types.ObjectId(companyId),
    { ...filter, ...options },
  );

  res.success(
    result,
    IndividualPropertiesResponseCodes.SUCCESS,
    'Property visitors fetched successfully',
  );
});

export const bulkDeleteIndividualProperties = catchAsync(
  async (req: Request, res: Response) => {
    const { propertyIds } = req.body;

    if (!Array.isArray(propertyIds) || propertyIds.length === 0) 
      throw new ApiError(
        httpStatus.BAD_REQUEST,
        'propertyIds must be a non-empty array',
      );
    

    for (const id of propertyIds) 
      if (!getObjectId(id)) 
        throw new ApiError(
          httpStatus.BAD_REQUEST,
          `Invalid property ID: ${id}`,
        );
      
    

    const result =
      await IndividualPropertiesService.bulkDeleteIndividualPropertiesById(
        propertyIds,
      );

    res.success(
      result,
      IndividualPropertiesResponseCodes.SUCCESS,
      'Bulk delete completed',
    );
  },
);

export const bulkUpdate = catchAsync(
  async (req: Request, res: Response) => {
    const { propertyIds, possessionStatus } = req.body;
    const updatedBy = req.user?.id;

    const result = await IndividualPropertiesService.bulkUpdate(
      propertyIds,
      possessionStatus,
      updatedBy,
    );

    res.success(
      result,
      IndividualPropertiesResponseCodes.SUCCESS,
      'Possession status updated successfully',
    );
  },
);

export const defaultFormValues = catchAsync(async (_req: Request, res: Response) => {
  const result = await IndividualPropertiesService.defaultFormValues();
  res.success(
    result,
    IndividualPropertiesResponseCodes.SUCCESS,
    'Property Fetched Successfully',
  );
});

export const getProjectNamesFromProperties = catchAsync(
  async (req: Request, res: Response) => {
    const companyId =
      (req.user?.company?.id as string | undefined) ||
      (req.query.companyId as string | undefined);

    if (!companyId)
      throw new ApiError(
        httpStatus.BAD_REQUEST,
        'companyId is required to fetch project names from properties',
      );

    const status = req.query.status as string | undefined;

    const result = await IndividualPropertiesService.getProjectNamesFromProperties(
      companyId,
      status,
    );

    res.success(
      result,
      IndividualPropertiesResponseCodes.SUCCESS,
      'Project names fetched successfully',
    );
  },
);

export const updatePropertySource = catchAsync(
  async (req: Request, res: Response) => {
    const { propertyId } = req.params;
    if (!getObjectId(propertyId))
      throw new ApiError(httpStatus.BAD_REQUEST, 'Invalid ID');

    const property = await IndividualPropertiesService.updatePropertySource(
      new mongoose.Types.ObjectId(propertyId),
      req.body.source,
    );

    res.success(
      property,
      IndividualPropertiesResponseCodes.SUCCESS,
      'Property source updated successfully',
    );
  },
);

export const generatePropertyNameAndDescription = catchAsync(
  async (req: Request, res: Response) => {
    const property = await IndividualPropertiesService.generatePropertyNameAndDescription(req.body);
    res.success(
      property,
      IndividualPropertiesResponseCodes.SUCCESS,
      'Property name and description generated successfully',
    );
  },
);

export const getPropertyVisibility = catchAsync(
  async (req: Request, res: Response) => {
    const { propertyId } = req.params;
    if (!getObjectId(propertyId))
      throw new ApiError(httpStatus.BAD_REQUEST, 'Invalid ID');

    const visibility = await IndividualPropertiesService.getPropertyVisibility(
      new mongoose.Types.ObjectId(propertyId),
    );
    if (!visibility)
      throw new ApiError(httpStatus.NOT_FOUND, 'Property not found');

    res.success(
      visibility,
      IndividualPropertiesResponseCodes.SUCCESS,
      'Property visibility fetched successfully',
    );
  },
);

export const updatePropertyVisibility = catchAsync(
  async (req: Request, res: Response) => {
    const { propertyId } = req.params;
    if (!getObjectId(propertyId))
      throw new ApiError(httpStatus.BAD_REQUEST, 'Invalid ID');

    const { visibleToUsers = [], visibleToTeams = [] } = req.body;

    const updatedProperty = await IndividualPropertiesService.updatePropertyVisibility(
      new mongoose.Types.ObjectId(propertyId),
      visibleToUsers,
      visibleToTeams,
    );
    if (!updatedProperty)
      throw new ApiError(httpStatus.NOT_FOUND, 'Property not found');

    res.success(
      {
        visibleToUsers: updatedProperty.visibleToUsers,
        visibleToTeams: updatedProperty.visibleToTeams,
      },
      IndividualPropertiesResponseCodes.SUCCESS,
      'Property visibility updated successfully',
    );
  },
);

export const bulkUpdatePropertyVisibility = catchAsync(
  async (req: Request, res: Response) => {
    const { propertyIds, isPublicVisibility } = req.body;

    if (!Array.isArray(propertyIds) || propertyIds.length === 0)
      throw new ApiError(
        httpStatus.BAD_REQUEST,
        'propertyIds must be a non-empty array',
      );

    for (const id of propertyIds)
      if (!getObjectId(id))
        throw new ApiError(
          httpStatus.BAD_REQUEST,
          `Invalid property ID: ${id}`,
        );

    const result = await IndividualPropertiesService.bulkUpdatePropertyVisibility(
      propertyIds,
      isPublicVisibility,
    );

    res.success(
      result,
      IndividualPropertiesResponseCodes.SUCCESS,
      'Bulk property visibility updated successfully',
    );
  },
);
