import { Request, Response } from 'express';

import * as projectService from '@/modules/project/project.service';
import * as rulesService from '@/modules/rules/rules.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 { UserType } from '@/shared/constants/enum.constant';
import { ApiError } from '@/shared/utils/errors';
import { defaultStatus } from '@/shared/utils/responseCode/httpStatusAlias';

const { ProjectResponseCodes } = responseCodes;

const enforceProjectEditRule = async (req: Request) => {
  const { userType, company } = req.user;

  if (userType === UserType.ADMIN || userType === UserType.SUPERADMIN) return;

  const companyId = company?.id;
  if (!companyId) return;

  const rules = (await rulesService.getRules(
    companyId,
    'edit_project_details',
  )) as Record<string, boolean>;

  if (!rules.edit_project_details)
    throw new ApiError(
      defaultStatus.FORBIDDEN,
      'Editing project details is disabled by company rules',
      true,
      '',
      ProjectResponseCodes.PROJECT_ERROR,
    );
};

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

  const projectPayload = {
    ...req.body,
    createdBy: userId,
    companyId,
  };

  const project = await projectService.createProject(projectPayload);

  return res.success(
    project,
    ProjectResponseCodes.SUCCESS,
    'Project Created Successfully',
  );
});

export const updateProject = catchAsync(async (req: Request, res: Response) => {
  await enforceProjectEditRule(req);
  const { id } = pick(req.params, ['id']);
  const { team } = req.user;
  const userId = getObjectId(req.user.id);
  const validId = getObjectId(id);
  const updatedProject = await projectService.updateProject(validId, req.body, userId, team);
  res.success(
    updatedProject,
    ProjectResponseCodes.SUCCESS,
    'Project Updated Successfully',
  );
});

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

    const project = await projectService.getProjectById(id, userId, team);

    res.success(
      project,
      ProjectResponseCodes.SUCCESS,
      'Project Fetched Successfully',
    );
  },
);

// GET /projects
// Query params (selected):
// - companyId: string | 'global'
//     * <uuid/ObjectId string>: returns only projects for that company
//     * 'global': returns projects across all companies (no company filter)
//     * omitted: current behavior is to not apply any company filter (same as global)
export const getProjects = catchAsync(async (req: Request, res: Response) => {
  const { team } = req.user;
  const filter = pick(req.query, [
    'propertyType',
    'createdBy',
    'creationMethod',
    'search',
    'status',
    'projectName',
    'city',
    'locality',
    'pincode',
    'price',
    'companyId',
  ]);
  // Add team filter to restrict to user's teams or projects without team
  if (team && team.length > 0) {
    filter.$or = [
      { team: { $in: team } },
      { team: { $exists: false } },
      { team: null }
    ];
  } else {
    // If user has no teams, only show projects without team
    filter.$or = [
      { team: { $exists: false } },
      { team: null }
    ];
  }

  const options = pick(req.query, [
    'sortBy',
    'limit',
    'page',
    'populate',
    'includeTimeStamps',
    'fields',
  ]);

  const projects = await projectService.queryProjects(filter, options);

  res.success(
    projects,
    ProjectResponseCodes.SUCCESS,
    'Projects Fetched Successfully',
  );
});

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

  await projectService.deleteProject(id, userId, team);

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

export const searchProjects = catchAsync(
  async (req: Request, res: Response) => {
    const companyId = req?.user?.company?.id ?? undefined;
    const { team } = req.user;
    const userId = getObjectId(req.user.id);
    const { q: searchQuery, limit = 10 } = req.query;

    const result = await projectService.searchProjectsWithAutocomplete(
      searchQuery as string,
      Number(limit),
      companyId,
      userId,
      team,
    );

    res.success(
      result,
      ProjectResponseCodes.SUCCESS,
      'Projects Search Successful',
    );
  },
);

export const projectAnalytics = catchAsync(
  async (req: Request, res: Response) => {
    const companyId = req?.user?.company?.id ?? undefined;

    const result = await projectService.projectAnalytics(companyId);

    res.success(
      result,
      ProjectResponseCodes.SUCCESS,
      'Projects Analytics Successful',
    );
  },
);

export const createFromSearch = catchAsync(
  async (req: Request, res: Response) => {
    let { searchQuery } = req.body;
    const { area, city, state } = req.body;
    const { id: userId } = req.user;
    const companyId = req.user.company.id;

    let forceCreate = false;

    const forceCreateMarker = '::forceCreate';
    if (
      typeof searchQuery === 'string' &&
      searchQuery.includes(forceCreateMarker)
    ) {
      forceCreate = true;
      searchQuery = searchQuery.replace(forceCreateMarker, '').trim();
    }

    const result = await projectService.createProjectFromSearch(
      searchQuery,
      userId,
      companyId,
      { area, city, state },
    );

    return res.success(
      result,
      ProjectResponseCodes.SUCCESS,
      result.message || 'Project Created Successfully',
    );
  },
);

export const updateProjectAmenities = catchAsync(
  async (req: Request, res: Response) => {
    await enforceProjectEditRule(req);
    const { id } = pick(req.params, ['id']);
    const { amenities } = req.body;
    const validId = getObjectId(id);

    const updatedProject = await projectService.updateProjectAmenities(
      validId,
      amenities,
    );

    res.success(
      updatedProject,
      ProjectResponseCodes.SUCCESS,
      'Project Amenities Updated Successfully',
    );
  },
);

export const updateProjectVisibility = catchAsync(
  async (req: Request, res: Response) => {
    await enforceProjectEditRule(req);
    const { id } = pick(req.params, ['id']);
    const { visibleToUsers = [], visibleToTeams = [] } = req.body;
    const validId = getObjectId(id);

    const updatedProject = await projectService.updateProjectVisibility(
      validId,
      visibleToUsers.map((userId: string) => getObjectId(userId)),
      visibleToTeams.map((teamId: string) => getObjectId(teamId)),
    );

    res.success(
      {
        visibleToUsers: updatedProject.visibleToUsers,
        visibleToTeams: updatedProject.visibleToTeams,
      },
      ProjectResponseCodes.SUCCESS,
      'Project Visibility Updated Successfully',
    );
  },
);

export const updateProjectsPublicVisibility = catchAsync(
  async (req: Request, res: Response) => {
    await enforceProjectEditRule(req);
    const { projectIds, isPublicVisibility } = req.body;

    const result = await projectService.updateProjectsPublicVisibility(
      projectIds,
      isPublicVisibility,
    );

    res.success(
      result,
      ProjectResponseCodes.SUCCESS,
      'Projects Public Visibility Updated Successfully',
    );
  },
);
