import Joi from 'joi';

import { objectId } from '@/shared/validations/custom.validation';
import { LeadBuyingPreference } from '@/shared/constants/enum.constant';

const optionalObjectIdString = () =>
  Joi.string()
    .trim()
    .allow('', null)
    .custom((value, helpers) => {
      if (value === '' || value == null) return value;
      return objectId(value, helpers);
    });

// Schema for a single row in the bulk lead import payload
export const bulkLeadRowSchema = Joi.object({
  firstName: Joi.string()
    .trim()
    .min(1)
    .required()
    .messages({
      'string.empty': 'firstName is required and cannot be empty',
      'any.required': 'firstName is required',
      'string.min': 'firstName is required and cannot be empty',
    }),
  lastName: Joi.string().trim().allow('', null),
  phone: Joi.string()
    .trim()
    .pattern(/^\d{10}$/)
    .required()
    .messages({
      'string.pattern.base':
        'phone must be exactly 10 digits (e.g. 9876543210); remove spaces and country codes',
      'any.required': 'phone is required',
      'string.empty': 'phone is required',
    }),
  secondaryPhone: Joi.string()
    .trim()
    .pattern(/^\d{10}$/)
    .allow('', null)
    .messages({
      'string.pattern.base': '"secondaryPhone" must be a 10-digit number',
    }),
  email: Joi.string()
    .trim()
    .email({ tlds: { allow: false } })
    .allow('', null)
    .messages({
      'string.email': 'email must be a valid address (e.g. name@domain.com)',
    }),
  secondaryEmail: Joi.string()
    .trim()
    .email({ tlds: { allow: false } })
    .allow('', null)
    .messages({
      'string.email': 'secondaryEmail must be a valid address',
    }),
  interestType: Joi.string()
    .valid('buy', 'sell', 'rent', 'lease')
    .allow('', null)
    .messages({
      'any.only':
        'interestType must be one of: buy, sell, rent, lease (when provided)',
    }),
  source: optionalObjectIdString(),
  notes: Joi.string().trim().allow('', null),
  remarks: Joi.string().trim().allow('', null),
  budget: Joi.when('interestType', {
    is: 'buy',
    then: Joi.number()
      .min(0)
      .required()
      .messages({
        'any.required': '"budget" is required when interestType is buy',
        'number.base': '"budget" must be a number when interestType is buy',
      }),
    otherwise: Joi.number().min(0).allow(null).optional(),
  }),
  securityDeposit: Joi.when('interestType', {
    is: 'rent',
    then: Joi.number()
      .min(0)
      .required()
      .messages({
        'any.required':
          '"securityDeposit" is required when interestType is rent',
        'number.base':
          '"securityDeposit" must be a number when interestType is rent',
      }),
    otherwise: Joi.number().min(0).allow(null).optional(),
  }),
  rentAmount: Joi.when('interestType', {
    is: 'rent',
    then: Joi.number()
      .min(0)
      .required()
      .messages({
        'any.required': '"rentAmount" is required when interestType is rent',
        'number.base': '"rentAmount" must be a number when interestType is rent',
      }),
    otherwise: Joi.number().min(0).allow(null).optional(),
  }),
  askingPrice: Joi.when('interestType', {
    is: 'sell',
    then: Joi.number()
      .min(0)
      .required()
      .messages({
        'any.required': '"askingPrice" is required when interestType is sell',
        'number.base':
          '"askingPrice" must be a number when interestType is sell',
      }),
    otherwise: Joi.number().min(0).allow(null).optional(),
  }),
  leaseAmount: Joi.when('interestType', {
    is: 'lease',
    then: Joi.number()
      .min(0)
      .required()
      .messages({
        'any.required': '"leaseAmount" is required when interestType is lease',
        'number.base':
          '"leaseAmount" must be a number when interestType is lease',
      }),
    otherwise: Joi.number().min(0).allow(null).optional(),
  }),
  leaseTermYears: Joi.when('interestType', {
    is: 'lease',
    then: Joi.alternatives()
      .try(
        Joi.number().min(0),
        Joi.string().trim().min(1),
      )
      .required()
      .messages({
        'any.required':
          '"leaseTermYears" is required when interestType is lease',
      }),
    otherwise: Joi.alternatives()
      .try(
        Joi.number().min(0),
        Joi.string().trim().allow('', null),
      )
      .allow(null)
      .optional(),
  }),
  project: optionalObjectIdString(),
  buyingPreference: Joi.string()
    .valid(...Object.values(LeadBuyingPreference))
    .allow('', null),
  assignedTo: optionalObjectIdString(),
}).unknown(true);

/** Row that passed Joi, with its original index in the request array (0-based). */
export type BulkLeadRowWithIndex = {
  row: Record<string, unknown>;
  originalRowIndex: number;
};

/**
 * Keeps only the first occurrence of each primary phone; later duplicates become error strings.
 * Uses 1-based row numbers in messages for end users.
 */
export const partitionDuplicatePhones = (
  joiValidRows: BulkLeadRowWithIndex[],
): { uniqueRows: BulkLeadRowWithIndex[]; duplicateErrors: string[] } => {
  const seenPhoneToFirstRow = new Map<string, number>();
  const uniqueRows: BulkLeadRowWithIndex[] = [];
  const duplicateErrors: string[] = [];

  for (const { row, originalRowIndex } of joiValidRows) {
    const phone =
      typeof row.phone === 'string' ? row.phone.trim() : String(row.phone ?? '');
    const firstIdx = seenPhoneToFirstRow.get(phone);
    if (firstIdx !== undefined) {
      duplicateErrors.push(
        `Row ${originalRowIndex + 1}: Phone "${phone}" is duplicated (also appears in row ${firstIdx + 1}).`,
      );
    } else {
      seenPhoneToFirstRow.set(phone, originalRowIndex);
      uniqueRows.push({ row, originalRowIndex });
    }
  }

  return { uniqueRows, duplicateErrors };
};

export const createBulkLead = {
  body: Joi.array().items(Joi.object().unknown(true)).min(1).required(),
};

export const bulkUpdateLead = {
  body: Joi.object().keys({
    ids: Joi.array().items(Joi.string().custom(objectId)).min(1).required(),
    updateData: Joi.object()
      .keys({
        assignedTo: Joi.string().custom(objectId),
        leadStage: Joi.string().custom(objectId),
        priority: Joi.string().valid('low', 'medium', 'high'),
        reason: Joi.string().optional(),
        // Add more fields here as needed in the future
      })
      .min(1)
      .required(),
  }),
};

export const createLead = {
  body: Joi.object().keys({
    contact: Joi.string().custom(objectId).required(),
    source: Joi.string().custom(objectId).required(),
    leadStage: Joi.string().custom(objectId).required(),
    priority: Joi.string().valid('low', 'medium', 'high').required(),
    interestType: Joi.string().valid('buy', 'sell', 'rent', 'lease').required(),
    company: Joi.string().custom(objectId).required(),
    assignedTo: Joi.string().custom(objectId),
    // Add other fields as needed
  }),
};

export const getLeads = {
  query: Joi.object().keys({
    leadStage: Joi.string(),
    companyName: Joi.string(),
    search: Joi.string(),
    createdBy: Joi.string(),
    interestType: Joi.string(),
    source: Joi.string(),
    assignedTo: Joi.string(),
    preferredCity: Joi.string(),
    preferredLocalities: Joi.string(),
    budget: Joi.string(),
    category: Joi.string(),
    subcategory: Joi.string(),
    project: Joi.string(),
    propertyType: Joi.string(),
    configuration: Joi.string(),
    sortBy: Joi.string(),
    limit: Joi.number().integer().min(1),
    page: Joi.number().integer().min(-1),
    fields: Joi.string(),
    populate: Joi.string(),
    includeTimeStamps: Joi.boolean(),
    alias: Joi.string(),
    view: Joi.string().valid('kanban'),
    stage: Joi.string(),
    totalActivity: Joi.string(),
  }),
};

export const getLead = {
  params: Joi.object().keys({
    id: Joi.string().custom(objectId).required(),
  }),
};

export const updateLead = {
  params: Joi.object().keys({
    id: Joi.string().custom(objectId).required(),
  }),
  body: Joi.object().keys({
    assignedTo: Joi.string().custom(objectId),
    leadStage: Joi.string().custom(objectId),
    priority: Joi.string().valid('low', 'medium', 'high'),
    // Add other updatable fields
  }),
};

export const deleteLead = {
  params: Joi.object().keys({
    id: Joi.string().custom(objectId).required(),
  }),
};

export const bulkDeleteLeads = {
  body: Joi.object().keys({
    ids: Joi.array().items(Joi.string().custom(objectId)).min(1).required(),
  }),
};

export const addSuggestedProperties = {
  params: Joi.object().keys({
    id: Joi.string().custom(objectId).required(),
  }),
  body: Joi.object().keys({
    propertyIds: Joi.array().items(Joi.string().custom(objectId)).min(1).required(),
  }),
};

export const moveLead = {
  params: Joi.object().keys({
    id: Joi.string().custom(objectId).required(),
  }),
  body: Joi.object().keys({
    stage: Joi.string().required(),
    beforeId: Joi.string().custom(objectId).optional(),
    afterId: Joi.string().custom(objectId).optional(),
  }),
};