import axios from 'axios';
import { getHttpClient, apiErrorMessage } from './http';
import { classificationLabelsForKey } from '../lib/crm-label-locale';
import { contactString } from '../utils/maskedContact';

export interface LeadListItem {
  id: number;
  reference?: string | null;
  subject?: string | null;
  clientId?: number | null;
  clientName?: string | null;
  clientPhone?: string | null;
  statusId?: number | null;
  statusName?: string | null;
  statusSlug?: string | null;
  classification?: string | null;
  engagementId?: number | null;
  engagementName?: string | null;
  engagementSlug?: string | null;
  engagementColor?: string | null;
  projectName?: string | null;
  sourceName?: string | null;
  budget?: number | null;
  rooms?: number | null;
  roomType?: string | null;
  lastAction?: string | null;
  lastActionLabel?: string | null;
  lastActionAt?: string | null;
  /** Legacy inbox2 « Traité / Non traité » (last_action). */
  isTreated?: boolean;
  agentNames?: string[];
  createdAt?: string | null;
  updatedAt?: string | null;
}

export interface ClientInterest {
  vocationId?: number | null;
  vocationName?: string | null;
  rooms?: number | null;
  floor?: number | null;
  budgetMin?: number | null;
  budgetMax?: number | null;
  budget?: number | null;
  propertyTypes?: { id: number; name: string }[];
  propertySubTypes?: { id: number; name: string }[];
  interestedProjects?: { id: number; name: string }[];
  lastProjectId?: number | null;
  lastProjectName?: string | null;
}

export interface LeadDetail {
  id: number;
  reference?: string | null;
  subject?: string | null;
  statusId?: number | null;
  statusName?: string | null;
  classification?: string | null;
  projectId?: number | null;
  projectName?: string | null;
  propertyId?: number | null;
  propertyTitle?: string | null;
  budget?: number | null;
  rooms?: number | null;
  roomType?: string | null;
  sourceName?: string | null;
  clientId?: number | null;
  clientName?: string | null;
  clientPhone?: string | null;
  clientEmail?: string | null;
  clientInterest?: ClientInterest | null;
  createdAt?: string | null;
  updatedAt?: string | null;
}

export interface LeadStatusOption {
  id: number;
  name: string;
  slug?: string | null;
}

export type LeadEngagementOption = {
  id: number;
  name: string;
  slug?: string | null;
  color?: string | null;
  ordre?: number | null;
};

export type LeadClassificationOption = {
  key: string;
  label: string;
  /** Optional per-locale labels from CRM (fr / en / ar). */
  labels?: Partial<Record<'fr' | 'en' | 'ar', string>>;
};

/** CRM default classifications (LegacyLeadWorkflowCatalog::classificationSeeds). */
export const DEFAULT_LEAD_CLASSIFICATIONS: LeadClassificationOption[] = [
  'prospect_qualifie',
  'prospect_froid',
  'prospect_chaud',
  'transaction_effectue',
  'non_qualifie',
  'non_interesse',
  'faux_numero',
  'perdu',
].map((key) => {
  const labels = classificationLabelsForKey(key);
  return {
    key,
    label: labels?.fr ?? key.replace(/_/g, ' '),
    labels: labels ?? undefined,
  };
});

/** Prefer Non traité → Non connecté → Qualifié → Chaud, then remaining by ordre. */
export const ENGAGEMENT_FILTER_ORDER = [
  'non-traite',
  'non-connecte',
  'qualifie',
  'chaud',
  'prospect_chaud',
  'interesse',
  'rappel',
  'froid',
];

export function sortEngagementsForFilters(
  rows: LeadEngagementOption[],
): LeadEngagementOption[] {
  const rank = (e: LeadEngagementOption) => {
    const s = `${e.slug ?? ''} ${e.name ?? ''}`
      .toLowerCase()
      .normalize('NFD')
      .replace(/[\u0300-\u036f]/g, '');
    for (let i = 0; i < ENGAGEMENT_FILTER_ORDER.length; i++) {
      const key = ENGAGEMENT_FILTER_ORDER[i].replace(/_/g, '-').replace(/-/g, '');
      const hay = s.replace(/-/g, '');
      if (hay.includes(key) || key.includes(hay)) return i;
    }
    return 100 + (e.ordre ?? 0);
  };
  return [...rows].sort((a, b) => rank(a) - rank(b));
}

/** Default list filter: engagement « Non traité » when present. */
export function findUntreatedEngagementId(rows: LeadEngagementOption[]): number | null {
  const hit = rows.find((e) => {
    const s = `${e.slug ?? ''} ${e.name ?? ''}`.toLowerCase().normalize('NFD').replace(/[\u0300-\u036f]/g, '');
    return s.includes('non-traite') || s.includes('non traite') || s.includes('nontraite');
  });
  return hit?.id ?? null;
}

/** Default list filter: étape « Non traité » when present. */
export function findUntreatedLeadStatusId(statuses: LeadStatusOption[]): number | null {
  const hit = statuses.find((s) => {
    const n = (s.name ?? '').trim().toLowerCase();
    const slug = (s.slug ?? '').toLowerCase();
    return (
      slug === 'non-traite' ||
      n === 'non traité' ||
      n === 'non traite' ||
      n.includes('non trait')
    );
  });
  return hit?.id ?? null;
}

export interface LeadListMeta {
  current_page: number;
  last_page: number;
  per_page: number;
  total: number;
}

export interface LeadsPage {
  data: LeadListItem[];
  meta: LeadListMeta;
  statuses: LeadStatusOption[];
  engagements: LeadEngagementOption[];
  classifications: LeadClassificationOption[];
  isPromoteur: boolean;
}

const PER_PAGE = 20;

function pickSourceName(row: Record<string, unknown>): string | null {
  const candidates = [
    row.sourceName,
    row.otherSourceName,
    row.source_name,
    row.other_source_name,
  ];
  for (const c of candidates) {
    if (c != null && String(c).trim() !== '') return String(c);
  }
  const client = (row.client ?? null) as Record<string, unknown> | null;
  if (client) {
    const osc = (client.otherSource ?? client.other_source) as Record<string, unknown> | null;
    if (osc?.name != null && String(osc.name).trim() !== '') return String(osc.name);
    if (osc?.text != null && String(osc.text).trim() !== '') return String(osc.text);
    const tags = client.otherSourcesTags ?? client.other_sources_tags ?? client.otherSources;
    if (Array.isArray(tags) && tags.length) {
      const names = tags
        .map((t) => {
          if (!t || typeof t !== 'object') return null;
          const o = t as Record<string, unknown>;
          return o.name ?? o.text ?? null;
        })
        .filter((n): n is string => n != null && String(n).trim() !== '')
        .map(String);
      if (names.length) return names.join(', ');
    }
    const src = (client.source ?? null) as Record<string, unknown> | null;
    if (src?.name != null && String(src.name).trim() !== '') return String(src.name);
  }
  return null;
}

function mapLegacyLead(row: Record<string, unknown>): LeadListItem {
  const client = (row.client ?? null) as Record<string, unknown> | null;
  const projects = Array.isArray(row.projects) ? (row.projects as Record<string, unknown>[]) : [];
  const agentNames = Array.isArray(row.agentNames)
    ? (row.agentNames as unknown[]).map(String).filter(Boolean)
    : Array.isArray(row.agent_names)
      ? (row.agent_names as unknown[]).map(String).filter(Boolean)
      : undefined;

  return {
    id: Number(row.id ?? 0),
    reference: row.reference != null ? String(row.reference) : null,
    subject: row.subject != null ? String(row.subject) : null,
    clientId: client?.id != null ? Number(client.id) : null,
    clientName: client
      ? String(client.name ?? `${client.first_name ?? ''} ${client.last_name ?? ''}`.trim())
      : null,
    clientPhone: contactString(client?.gsm),
    statusName: null,
    classification: null,
    projectName: projects[0]?.name != null ? String(projects[0].name) : null,
    sourceName: pickSourceName(row),
    agentNames,
    createdAt: row.created_at != null ? String(row.created_at) : null,
    updatedAt: row.created_at != null ? String(row.created_at) : null,
  };
}

function asNamedList(value: unknown): { id: number; name: string }[] {
  if (!Array.isArray(value)) return [];
  return value
    .map((row) => {
      if (!row || typeof row !== 'object') return null;
      const o = row as Record<string, unknown>;
      const id = Number(o.id ?? 0);
      const name = o.name != null ? String(o.name) : '';
      if (!name) return null;
      return { id, name };
    })
    .filter((x): x is { id: number; name: string } => x != null);
}

function normalizeClientInterest(raw: Record<string, unknown> | null | undefined): ClientInterest | null {
  if (!raw) return null;
  return {
    vocationId: (raw.vocationId ?? raw.interestedByVocationId) as number | null,
    vocationName: (raw.vocationName ?? raw.interestedByVocationName) as string | null,
    rooms: (raw.rooms ?? raw.interestedByRooms) as number | null,
    floor: (raw.floor ?? raw.interestedByFloor) as number | null,
    budgetMin: raw.budgetMin as number | null,
    budgetMax: raw.budgetMax as number | null,
    budget: raw.budget as number | null,
    propertyTypes: asNamedList(raw.propertyTypes ?? raw.interestedPropertyTypes),
    propertySubTypes: asNamedList(raw.propertySubTypes ?? raw.interestedPropertySubTypes),
    interestedProjects: asNamedList(raw.interestedProjects),
    lastProjectId: (raw.lastProjectId ?? raw.lastInterestedByProjectId) as number | null,
    lastProjectName: (raw.lastProjectName ?? raw.lastInterestedByProjectName) as string | null,
  };
}

function resolveClientId(raw: Record<string, unknown>): number | null {
  if (raw.clientId != null && Number(raw.clientId) > 0) return Number(raw.clientId);
  if (raw.client_id != null && Number(raw.client_id) > 0) return Number(raw.client_id);
  const nested = (raw.client ?? null) as Record<string, unknown> | null;
  if (nested?.id != null && Number(nested.id) > 0) return Number(nested.id);
  return null;
}

function mapLeadDetail(raw: Record<string, unknown>): LeadDetail {
  const interest = normalizeClientInterest(
    (raw.clientInterest ?? raw.client_interest) as Record<string, unknown> | null | undefined,
  );
  const nested = (raw.client ?? null) as Record<string, unknown> | null;

  return {
    id: Number(raw.id),
    reference: raw.reference != null ? String(raw.reference) : null,
    subject: raw.subject != null ? String(raw.subject) : null,
    statusId: raw.statusId != null ? Number(raw.statusId) : raw.status_id != null ? Number(raw.status_id) : null,
    statusName:
      raw.statusName != null
        ? String(raw.statusName)
        : raw.status_name != null
          ? String(raw.status_name)
          : null,
    classification:
      raw.classification != null
        ? String(raw.classification)
        : raw.lead_classification != null
          ? String(raw.lead_classification)
          : null,
    projectId: raw.projectId != null ? Number(raw.projectId) : null,
    projectName: raw.projectName != null ? String(raw.projectName) : null,
    propertyId: raw.propertyId != null ? Number(raw.propertyId) : null,
    propertyTitle: raw.propertyTitle != null ? String(raw.propertyTitle) : null,
    budget: raw.budget != null ? Number(raw.budget) : null,
    rooms: raw.rooms != null ? Number(raw.rooms) : null,
    roomType: raw.roomType != null ? String(raw.roomType) : raw.room_type != null ? String(raw.room_type) : null,
    sourceName: pickSourceName(raw),
    clientId: resolveClientId(raw),
    clientName:
      raw.clientName != null
        ? String(raw.clientName)
        : nested
          ? String(nested.name ?? `${nested.first_name ?? ''} ${nested.last_name ?? ''}`.trim())
          : null,
    clientPhone: contactString(raw.clientPhone ?? nested?.gsm),
    clientEmail:
      raw.clientEmail != null
        ? String(raw.clientEmail)
        : nested?.email != null
          ? String(nested.email)
          : null,
    clientInterest: interest,
    createdAt: raw.createdAt != null ? String(raw.createdAt) : null,
    updatedAt: raw.updatedAt != null ? String(raw.updatedAt) : null,
  };
}

function isMissingEndpoint(err: unknown): boolean {
  if (!axios.isAxiosError(err)) return false;
  const status = err.response?.status;
  if (status === 405) return true;
  if (status !== 404) return false;
  // JSON API 404 (lead not found) is not a missing route
  const data = err.response?.data;
  if (data && typeof data === 'object' && ('message' in data || 'data' in data || 'error' in data)) {
    return false;
  }
  // Symfony / Apache HTML 404 page → endpoint not deployed
  if (typeof data === 'string' && (data.includes('<html') || data.includes('<!DOCTYPE'))) {
    return true;
  }
  return typeof data === 'string' || data == null;
}

function apiNotFoundMessage(err: unknown, fallback: string): string {
  if (axios.isAxiosError(err)) {
    const data = err.response?.data;
    if (data && typeof data === 'object' && typeof (data as { message?: unknown }).message === 'string') {
      return String((data as { message: string }).message);
    }
  }
  return fallback;
}

async function fetchLeadsPageLegacyFallback(
  page: number,
  opts: { q?: string; statusId?: number | null; leadClassification?: string | null },
): Promise<LeadsPage> {
  const { client } = await getHttpClient();
  const { data } = await client.get<Record<string, unknown>>('/leads/list', {
    params: { page, num_items_per_page: PER_PAGE },
  });
  const items = Array.isArray(data.items) ? (data.items as Record<string, unknown>[]) : [];
  const total = Number(data.totalCount ?? data.total_count ?? items.length);
  const lastPage = Math.max(1, Math.ceil(total / PER_PAGE));
  let rows = items.map(mapLegacyLead);
  const q = opts.q?.trim().toLowerCase();
  if (q) {
    rows = rows.filter(
      (r) =>
        (r.clientName ?? '').toLowerCase().includes(q) ||
        (r.subject ?? '').toLowerCase().includes(q) ||
        (r.clientPhone ?? '').includes(q),
    );
  }
  return {
    data: rows,
    meta: { current_page: page, last_page: lastPage, per_page: PER_PAGE, total },
    statuses: [],
    engagements: [],
    classifications: [],
    isPromoteur: false,
  };
}

export async function fetchLeadsPage(
  page = 1,
  opts: {
    q?: string;
    statusId?: number | null;
    engagementId?: number | null;
    leadClassification?: string | null;
    /** Legacy inbox2 form[status]: 1 = Traité, 2 = Non traité */
    treated?: 1 | 2 | null;
    /** Filter leads for one client (1 client → many leads). */
    clientId?: number | null;
  } = {},
): Promise<LeadsPage> {
  const { client, config } = await getHttpClient();

  try {
    const { data } = await client.get<{
      data: LeadListItem[];
      meta: LeadListMeta;
      filters?: {
        statuses?: LeadStatusOption[];
        engagements?: LeadEngagementOption[];
        classifications?: LeadClassificationOption[];
        isPromoteur?: boolean;
      };
    }>('/mobile/threads', {
      params: {
        page,
        per_page: PER_PAGE,
        q: opts.q?.trim() || undefined,
        status_id: opts.statusId || undefined,
        engagement_id: opts.engagementId || undefined,
        lead_classification: opts.leadClassification || undefined,
        treated: opts.treated ?? undefined,
        client_id: opts.clientId || undefined,
      },
    });

    const rows = Array.isArray(data.data) ? data.data : [];
    const classifications = (
      Array.isArray(data.filters?.classifications) ? data.filters!.classifications! : []
    ).map((c) => {
      const catalog = classificationLabelsForKey(c.key);
      return {
        ...c,
        label: c.label || catalog?.fr || c.key.replace(/_/g, ' '),
        labels: c.labels ?? catalog ?? undefined,
      };
    });
    const engagements = Array.isArray(data.filters?.engagements) ? data.filters!.engagements! : [];
    return {
      data: rows.map((r) => ({
        ...r,
        clientPhone: contactString(r.clientPhone),
        sourceName: pickSourceName(r as unknown as Record<string, unknown>) ?? r.sourceName ?? null,
      })),
      meta: data.meta ?? { current_page: page, last_page: 1, per_page: PER_PAGE, total: rows.length },
      statuses: Array.isArray(data.filters?.statuses) ? data.filters!.statuses! : [],
      engagements,
      classifications,
      isPromoteur: Boolean(data.filters?.isPromoteur),
    };
  } catch (err) {
    if (config.mode === 'legacy' && isMissingEndpoint(err)) {
      return fetchLeadsPageLegacyFallback(page, opts);
    }
    throw err;
  }
}

export async function fetchLeadDetail(id: number): Promise<LeadDetail> {
  const { client, config } = await getHttpClient();
  // Prefer mobile route on both CRMs (parity + client-less Contact leads).
  const primaryPath = `/mobile/threads/${id}`;
  const fallbackPath = config.mode === 'new' ? `/threads/${id}` : null;
  try {
    let raw: Record<string, unknown>;
    try {
      const { data } = await client.get<{ data: Record<string, unknown> }>(primaryPath);
      raw = data.data;
    } catch (err) {
      if (fallbackPath && isMissingEndpoint(err)) {
        const { data } = await client.get<{ data: Record<string, unknown> }>(fallbackPath);
        raw = data.data;
      } else {
        throw err;
      }
    }
    const detail = mapLeadDetail(raw);

    if (config.mode === 'new' && detail.clientId && !detail.clientInterest) {
      try {
        const clientRes = await client.get<{ data: Record<string, unknown> }>(
          `/clients/${detail.clientId}`,
        );
        const c = clientRes.data.data;
        detail.clientInterest = normalizeClientInterest({
          interestedByVocationId: c.interestedByVocationId,
          interestedByVocationName: c.interestedByVocationName,
          interestedByRooms: c.interestedByRooms,
          interestedByFloor: c.interestedByFloor,
          budgetMin: c.budgetMin,
          budgetMax: c.budgetMax,
          budget: c.budget,
          interestedPropertyTypes: c.interestedPropertyTypes,
          interestedPropertySubTypes: c.interestedPropertySubTypes,
          interestedProjects: c.interestedProjects,
          lastInterestedByProjectId: c.lastInterestedByProjectId,
          lastInterestedByProjectName: c.lastInterestedByProjectName,
        } as Record<string, unknown>);
      } catch {
        // optional enrichment
      }
    }

    return detail;
  } catch (err) {
    if (isMissingEndpoint(err)) {
      throw new Error('Mise à jour du CRM requise pour voir le détail du lead.');
    }
    if (axios.isAxiosError(err) && err.response?.status === 404) {
      throw new Error(apiNotFoundMessage(err, 'Lead introuvable.'));
    }
    throw err;
  }
}

/** Mark lead as treated (last_action.treated) — same as web journal. */
export async function markLeadTreated(id: number): Promise<void> {
  const { client, config } = await getHttpClient();
  if (config.mode === 'legacy') {
    // Legacy REST may not expose mark-treated; journal save still applies classification.
    return;
  }
  try {
    await client.post(`/threads/${id}/mark-treated`);
  } catch (err) {
    throw new Error(apiErrorMessage(err));
  }
}

/** Update this lead only (Thread / Message). Never mutates the shared Client fiche. */
export async function updateLead(
  id: number,
  payload: {
    subject?: string;
    status_id?: number | null;
    project_id?: number | null;
    property_id?: number | null;
    budget?: number | null;
    rooms?: number | null;
    room_type?: string | null;
  },
): Promise<LeadDetail> {
  const { client, config } = await getHttpClient();
  const primaryPath = `/mobile/threads/${id}`;
  const fallbackPath = config.mode === 'new' ? `/threads/${id}` : null;

  const threadFields = {
    subject: payload.subject,
    status_id: payload.status_id,
    project_id: payload.project_id,
    property_id: payload.property_id,
    budget: payload.budget,
    rooms: payload.rooms,
    room_type: payload.room_type,
  };

  try {
    let data: { data: Record<string, unknown> };
    try {
      const res = await client.patch<{ data: Record<string, unknown> }>(primaryPath, threadFields);
      data = res.data;
    } catch (err) {
      if (fallbackPath && isMissingEndpoint(err)) {
        const res = await client.patch<{ data: Record<string, unknown> }>(fallbackPath, threadFields);
        data = res.data;
      } else {
        throw err;
      }
    }

    return mapLeadDetail(data.data);
  } catch (err) {
    throw new Error(apiErrorMessage(err));
  }
}

export { apiErrorMessage };
