import axios from 'axios';
import { getHttpClient, apiErrorMessage } from './http';
import { contactString } from '../utils/maskedContact';
import { detectDefaultCountryCode } from '../utils/phone';

function isMissingEndpoint(err: unknown): boolean {
  return axios.isAxiosError(err) && (err.response?.status === 404 || err.response?.status === 405);
}

export interface ProspectPayload {
  firstName: string;
  lastName: string;
  gsm?: string | null;
  gsmPrefix?: string;
  email?: string | null;
  address?: string | null;
  city?: string | null;
  note?: string | null;
  genreId?: number | null;
  engagementId?: number | null;
  otherSourceIds?: number[];
  interestedProjectIds?: number[];
  projectId?: number | null;
  /** Numeric rooms or studio/shop/office (create-new parity). */
  interestedByRooms?: string | null;
  budgetMax?: number | null;
  interestedByVocationId?: number | null;
  vocation?: 'vente' | 'location' | null;
  agentId?: number | null;
  /** Omit agent_id so CRM can apply round-robin by source. */
  leaveAgentForRoundRobin?: boolean;
  interestedByCountryId?: number | null;
  interestedGouvernoratIds?: number[];
  interestedDelegationIds?: number[];
  interestedLocaliteIds?: number[];
  interestedDistrictIds?: number[];
}

export interface SavedProspect {
  id: number;
  reference: string | null;
  name: string;
  /** Inbox2 Thread id when CRM created a lead with the contact. */
  threadId?: number | null;
  /** True when phone matched an existing client and a new lead was attached. */
  linkedExistingClient?: boolean;
}

function toApiPayload(p: ProspectPayload, opts?: { omitPhone?: boolean }): Record<string, unknown> {
  const defaultCc = detectDefaultCountryCode();
  const projectIds =
    p.interestedProjectIds && p.interestedProjectIds.length > 0
      ? p.interestedProjectIds
      : p.projectId
        ? [p.projectId]
        : [];
  const body: Record<string, unknown> = {
    first_name: p.firstName.trim(),
    last_name: p.lastName.trim() || p.firstName.trim(),
    email: p.email?.trim() || null,
    address: p.address?.trim() || null,
    city: p.city?.trim() || null,
    note: p.note?.trim() || null,
    genre_id: p.genreId ?? null,
    engagement_id: p.engagementId ?? null,
    other_source_ids: p.otherSourceIds ?? [],
    interested_project_ids: projectIds,
    project_id: p.projectId ?? projectIds[0] ?? null,
    interested_by_rooms: p.interestedByRooms?.trim() || null,
    budget_max: p.budgetMax ?? null,
    interested_by_vocation_id: p.interestedByVocationId ?? null,
    vocation: p.vocation ?? null,
    interested_by_country_id: p.interestedByCountryId ?? null,
    interested_gouvernorat_ids: p.interestedGouvernoratIds ?? [],
    interested_delegation_ids: p.interestedDelegationIds ?? [],
    interested_localite_ids: p.interestedLocaliteIds ?? [],
    interested_district_ids: p.interestedDistrictIds ?? [],
  };
  // Phone is immutable once saved — omit on update so CRM never overwrites gsm.
  if (!opts?.omitPhone) {
    body.gsm = p.gsm?.trim() || null;
    body.gsm_prefix = p.gsmPrefix?.trim() || defaultCc;
  }
  // Always send responsible agent when chosen (CRM "Agents responsables").
  // Omit only for explicit source round-robin.
  if (!p.leaveAgentForRoundRobin && p.agentId != null && p.agentId > 0) {
    body.agent_id = p.agentId;
  }
  return body;
}

function mapSaved(raw: Record<string, unknown>): SavedProspect {
  return {
    id: Number(raw.id),
    reference: raw.reference != null ? String(raw.reference) : null,
    name:
      [raw.first_name, raw.last_name].filter(Boolean).join(' ') ||
      String(raw.name ?? '') ||
      'Contact',
    threadId: raw.thread_id != null ? Number(raw.thread_id) : null,
    linkedExistingClient: Boolean(raw.linked_existing_client),
  };
}

/** 422 duplicate (both CRMs return code client_identity_duplicate) → readable message. */
function prospectErrorMessage(err: unknown): string {
  if (axios.isAxiosError(err) && err.response?.status === 422) {
    const data = err.response.data as { message?: string; code?: string } | undefined;
    if (data?.message) return data.message;
  }
  return apiErrorMessage(err);
}

export interface ClientIdentityMatch {
  exists: boolean;
  clientId: number | null;
  name: string | null;
  otherSourceIds: number[];
  agentId: number | null;
  agentName: string | null;
  projectId: number | null;
}

/** Phone uniqueness check (create-new parity). */
export async function checkClientIdentity(opts: {
  gsm: string;
  gsmPrefix: string;
}): Promise<ClientIdentityMatch> {
  const empty: ClientIdentityMatch = {
    exists: false,
    clientId: null,
    name: null,
    otherSourceIds: [],
    agentId: null,
    agentName: null,
    projectId: null,
  };
  const gsm = opts.gsm.replace(/\D/g, '');
  if (gsm.length < 6) return empty;

  const { client, config } = await getHttpClient();
  try {
    if (config.mode === 'legacy') {
      const { data } = await client.get<{ data: Record<string, unknown> }>(
        '/mobile/clients/check-identity',
        { params: { gsm, gsm_prefix: opts.gsmPrefix.replace(/\D/g, '') } },
      );
      const d = data.data ?? {};
      const sources = Array.isArray(d.otherSourceIds)
        ? (d.otherSourceIds as unknown[]).map(Number).filter((n) => n > 0)
        : Array.isArray(d.other_source_ids)
          ? (d.other_source_ids as unknown[]).map(Number).filter((n) => n > 0)
          : [];
      return {
        exists: Boolean(d.exists ?? d.available === false),
        clientId: d.clientId != null ? Number(d.clientId) : d.client_id != null ? Number(d.client_id) : null,
        name: d.name != null ? String(d.name) : null,
        otherSourceIds: sources,
        agentId: d.agentId != null ? Number(d.agentId) : d.agent_id != null ? Number(d.agent_id) : null,
        agentName: d.agentName != null ? String(d.agentName) : null,
        projectId:
          d.projectId != null ? Number(d.projectId) : d.project_id != null ? Number(d.project_id) : null,
      };
    }
    const { data } = await client.get<{
      data: {
        available?: boolean;
        conflict?: { clientId?: number; clientName?: string } | null;
      };
    }>('/clients/check-identity', {
      params: {
        gsm,
        gsm_prefix: opts.gsmPrefix.replace(/\D/g, ''),
      },
    });
    const row = data.data;
    if (row?.available === false && row.conflict?.clientId) {
      return {
        ...empty,
        exists: true,
        clientId: Number(row.conflict.clientId),
        name: row.conflict.clientName != null ? String(row.conflict.clientName) : null,
      };
    }
    return empty;
  } catch (err) {
    if (isMissingEndpoint(err)) return empty;
    return empty;
  }
}

export async function createProspect(payload: ProspectPayload): Promise<SavedProspect> {
  const { client, config } = await getHttpClient();
  const body = toApiPayload(payload);
  const path = config.mode === 'legacy' ? '/mobile/clients' : '/clients';
  try {
    const { data } = await client.post<{ data: Record<string, unknown> }>(path, body);
    return mapSaved(data.data);
  } catch (err) {
    if (config.mode === 'legacy' && isMissingEndpoint(err)) {
      throw new Error('Mise à jour du CRM requise pour créer un contact depuis le mobile.');
    }
    throw new Error(prospectErrorMessage(err));
  }
}

export async function updateProspect(
  id: number,
  payload: ProspectPayload,
): Promise<SavedProspect> {
  const { client, config } = await getHttpClient();
  // Full form body except phone (locked after create).
  const body = toApiPayload(payload, { omitPhone: true });

  const path = config.mode === 'legacy' ? `/mobile/clients/${id}` : `/clients/${id}`;
  try {
    const { data } = await client.patch<{ data: Record<string, unknown> }>(path, body);
    return mapSaved(data.data);
  } catch (err) {
    if (axios.isAxiosError(err) && (err.response?.status === 405 || err.response?.status === 404)) {
      try {
        const { data } = await client.put<{ data: Record<string, unknown> }>(path, body);
        return mapSaved(data.data);
      } catch (err2) {
        if (config.mode === 'legacy' && isMissingEndpoint(err2)) {
          throw new Error('Mise à jour du CRM requise pour modifier un contact depuis le mobile.');
        }
        throw new Error(prospectErrorMessage(err2));
      }
    }
    if (config.mode === 'legacy' && isMissingEndpoint(err)) {
      throw new Error('Mise à jour du CRM requise pour modifier un contact depuis le mobile.');
    }
    throw new Error(prospectErrorMessage(err));
  }
}

export { contactString };
