import { getHttpClient, apiErrorMessage, flattenFormData } from './http';
import { formatTimelineDate, normalizeDateInput, parseDateMs } from '../utils/activityLabels';
import { htmlToPlainText } from '../utils/html';
import { i18n, localeForLanguage } from '../i18n';

/** Legacy / CRM: 1 confirmed, 2 canceled, 3 carried out. */
export type EventOutcome = 'done' | 'canceled' | 'postponed';

export interface EventDetail {
  id: number;
  title: string;
  description: string | null;
  resultComment: string | null;
  eventStatus: number | null;
  startAt: string | null;
  endAt: string | null;
  dateLabel: string | null;
  timeLabel: string | null;
  endLabel: string | null;
  allDay: boolean;
  type: string | null;
  kindLabel: 'rappel' | 'event';
  color: string | null;
  clientId: number | null;
  clientName: string | null;
  clientPhone: string | null;
  agentId: number | null;
  agentName: string | null;
  agents: string[];
  propertyId: number | null;
  propertyTitle: string | null;
  projectName: string | null;
  visitId: number | null;
}

export function eventStatusToOutcome(status: number | null | undefined): EventOutcome | null {
  if (status === 3) return 'done';
  if (status === 2) return 'canceled';
  return null;
}

function extractResultComment(raw: Record<string, unknown>): string | null {
  if (raw.resultComment != null) {
    const s = htmlToPlainText(String(raw.resultComment)).trim();
    if (s) return s;
  }
  if (raw.result_comment != null) {
    const s = htmlToPlainText(String(raw.result_comment)).trim();
    if (s) return s;
  }
  const comments = raw.comments;
  if (Array.isArray(comments) && comments.length > 0) {
    const last = comments[comments.length - 1];
    if (typeof last === 'string') {
      const s = htmlToPlainText(last).trim();
      return s || null;
    }
    if (last && typeof last === 'object') {
      const o = last as Record<string, unknown>;
      const body = o.body ?? o.comment ?? o.text;
      if (body != null) {
        const s = htmlToPlainText(String(body)).trim();
        return s || null;
      }
    }
  }
  return null;
}

function personName(raw?: Record<string, unknown> | null): string | null {
  if (!raw) return null;
  const n = String(
    raw.name ??
      raw.full_name ??
      [raw.first_name ?? raw.firstName ?? raw.firstname, raw.last_name ?? raw.lastName ?? raw.lastname]
        .filter(Boolean)
        .join(' '),
  ).trim();
  return n || null;
}

function coerceDateString(value: unknown): string | null {
  if (value == null || value === '') return null;
  if (typeof value === 'object') {
    const o = value as Record<string, unknown>;
    if (typeof o.date === 'string') return coerceDateString(o.date);
    if (typeof o.datetime === 'string') return coerceDateString(o.datetime);
    return null;
  }
  const normalized = normalizeDateInput(value);
  return normalized || String(value).trim() || null;
}

function formatDateOnly(value: string | null): string | null {
  if (!value) return null;
  const ms = parseDateMs(value);
  if (Number.isNaN(ms)) return value.slice(0, 10);
  return new Date(ms).toLocaleDateString(localeForLanguage(), {
    weekday: 'long',
    year: 'numeric',
    month: 'long',
    day: 'numeric',
  });
}

function formatTimeOnly(value: string | null, allDay: boolean): string | null {
  if (!value || allDay) return null;
  const ms = parseDateMs(value);
  if (Number.isNaN(ms)) {
    const m = value.match(/(\d{2}:\d{2})/);
    return m ? m[1] : null;
  }
  return new Date(ms).toLocaleTimeString(localeForLanguage(), { hour: '2-digit', minute: '2-digit' });
}

function detectRappel(type: string | null, title: string): boolean {
  const s = `${type ?? ''} ${title}`.toLowerCase();
  return /rappel|appel|call_reminder|reminder|call/.test(s);
}

function enrichLabels(ev: Omit<EventDetail, 'dateLabel' | 'timeLabel' | 'endLabel'>): EventDetail {
  return {
    ...ev,
    dateLabel: formatDateOnly(ev.startAt),
    timeLabel: formatTimeOnly(ev.startAt, ev.allDay),
    endLabel: formatTimeOnly(ev.endAt, ev.allDay),
  };
}

function mapNewEvent(raw: Record<string, unknown>): EventDetail {
  const type = raw.type != null ? String(raw.type) : null;
  const title = String(raw.title ?? '');
  const agentsRaw = Array.isArray(raw.agents) ? (raw.agents as Record<string, unknown>[]) : [];
  const agentNames = agentsRaw
    .map((a) => personName(a) || (a.name != null ? String(a.name) : null))
    .filter(Boolean) as string[];
  const startAt = coerceDateString(raw.startAt ?? raw.start_at ?? raw.startDate);
  const endAt = coerceDateString(raw.endAt ?? raw.end_at ?? raw.endDate);
  const agentName =
    (raw.agentName != null ? String(raw.agentName).trim() : '') ||
    personName(raw.agent as Record<string, unknown> | undefined) ||
    agentNames[0] ||
    null;

  const eventStatus =
    raw.eventStatus != null
      ? Number(raw.eventStatus)
      : raw.event_status != null
        ? Number(raw.event_status)
        : null;

  return enrichLabels({
    id: Number(raw.id),
    title,
    description: raw.description != null ? htmlToPlainText(String(raw.description)) : null,
    resultComment: extractResultComment(raw),
    eventStatus: Number.isFinite(eventStatus as number) ? eventStatus : null,
    startAt,
    endAt,
    allDay: Boolean(raw.allDay ?? raw.all_day),
    type,
    kindLabel: detectRappel(type, title) ? 'rappel' : 'event',
    color: raw.color != null ? String(raw.color) : null,
    clientId: raw.clientId != null ? Number(raw.clientId) : null,
    clientName: raw.clientName != null ? String(raw.clientName).trim() || null : null,
    clientPhone:
      raw.clientPhone != null
        ? String(raw.clientPhone)
        : raw.clientGsm != null
          ? String(raw.clientGsm)
          : null,
    agentId: raw.agentId != null ? Number(raw.agentId) : null,
    agentName,
    agents: agentNames,
    propertyId: raw.propertyId != null ? Number(raw.propertyId) : null,
    propertyTitle: raw.propertyTitle != null ? String(raw.propertyTitle) : null,
    projectName: raw.projectName != null ? String(raw.projectName) : null,
    visitId: raw.visitId != null ? Number(raw.visitId) : null,
  });
}

function mapLegacyEvent(raw: Record<string, unknown>): EventDetail {
  const client = raw.client as Record<string, unknown> | undefined;
  const agent = (raw.primaryAgent ?? raw.agent) as Record<string, unknown> | undefined;
  const property = raw.property as Record<string, unknown> | undefined;
  const properties = Array.isArray(raw.properties) ? (raw.properties as Record<string, unknown>[]) : [];
  const projects = Array.isArray(raw.projects) ? (raw.projects as Record<string, unknown>[]) : [];
  const agentsList = Array.isArray(raw.agents) ? (raw.agents as Record<string, unknown>[]) : [];
  const typeObj = raw.type as Record<string, unknown> | string | undefined;
  const typeName =
    typeof typeObj === 'object' && typeObj != null
      ? String(typeObj.name ?? typeObj.titre ?? '')
      : typeObj != null
        ? String(typeObj)
        : '';
  const title = String(raw.title ?? '');
  const startAt = coerceDateString(raw.startDate ?? raw.start_at ?? raw.startAt ?? raw.start);
  const endAt = coerceDateString(raw.endDate ?? raw.end_at ?? raw.endAt ?? raw.end);
  const prop = property ?? properties[0];
  const propProject = prop?.project as Record<string, unknown> | undefined;
  const projet = projects[0] ?? propProject;
  const agentNames = agentsList.map((a) => personName(a)).filter(Boolean) as string[];
  const eventStatus =
    raw.eventStatus != null
      ? Number(raw.eventStatus)
      : raw.event_status != null
        ? Number(raw.event_status)
        : null;

  return enrichLabels({
    id: Number(raw.id),
    title,
    description: raw.description != null ? htmlToPlainText(String(raw.description)) : null,
    resultComment: extractResultComment(raw),
    eventStatus: Number.isFinite(eventStatus as number) ? eventStatus : null,
    startAt,
    endAt,
    allDay: Boolean(raw.allDay ?? raw.all_day),
    type: typeName || null,
    kindLabel: detectRappel(typeName, title) ? 'rappel' : 'event',
    color: raw.backgroundColor != null ? String(raw.backgroundColor) : raw.color != null ? String(raw.color) : null,
    clientId: client?.id != null ? Number(client.id) : raw.clientId != null ? Number(raw.clientId) : null,
    clientName: personName(client),
    clientPhone:
      client?.gsm != null
        ? String(client.gsm)
        : client?.phone != null
          ? String(client.phone)
          : null,
    agentId: agent?.id != null ? Number(agent.id) : null,
    agentName: personName(agent) || agentNames[0] || null,
    agents: agentNames,
    propertyId: prop?.id != null ? Number(prop.id) : null,
    propertyTitle: prop
      ? String(prop.name ?? prop.title ?? prop.reference ?? '') || null
      : null,
    projectName: projet
      ? String(projet.titre ?? projet.name ?? projet.title ?? '') || null
      : null,
    visitId:
      raw.visiteClient != null
        ? Number((raw.visiteClient as Record<string, unknown>).id)
        : raw.visitId != null
          ? Number(raw.visitId)
          : null,
  });
}

/** Prefer API labels; fall back to agenda date/time params. */
export function applyEventFallbacks(
  ev: EventDetail,
  fallback?: { date?: string | null; time?: string | null; title?: string | null; kind?: string | null },
): EventDetail {
  const startAt =
    ev.startAt ||
    (fallback?.date
      ? `${fallback.date}${fallback.time ? `T${fallback.time.length === 5 ? fallback.time : fallback.time}` : 'T00:00:00'}`
      : null);
  const next = enrichLabels({
    ...ev,
    title: ev.title || fallback?.title || ev.title,
    startAt,
    kindLabel:
      fallback?.kind === 'rappel' || fallback?.kind === 'event'
        ? fallback.kind
        : ev.kindLabel,
  });
  if (!next.timeLabel && fallback?.time) {
    next.timeLabel = fallback.time;
  }
  if (!next.dateLabel && fallback?.date) {
    next.dateLabel = formatDateOnly(fallback.date);
  }
  return next;
}

export function formatEventWhen(ev: EventDetail): string {
  const parts = [ev.dateLabel, ev.allDay ? 'Journée' : ev.timeLabel].filter(Boolean);
  if (parts.length) return parts.join(' · ');
  if (ev.startAt) return formatTimelineDate(ev.startAt) || ev.startAt;
  return '—';
}

export async function fetchEventDetail(id: number): Promise<EventDetail> {
  const { client, config } = await getHttpClient();
  try {
    if (config.mode === 'legacy') {
      const { data } = await client.get<Record<string, unknown>>(`/events/${id}/details`);
      if (!data || data.id == null) {
        throw new Error(i18n.t('event.notFound'));
      }
      return mapLegacyEvent(data);
    }
    const { data } = await client.get<{ data: Record<string, unknown> }>(`/events/${id}`);
    return mapNewEvent(data.data ?? {});
  } catch (err) {
    throw new Error(apiErrorMessage(err) || i18n.t('event.loadError'));
  }
}

export async function updateEventOutcome(
  id: number,
  payload: {
    outcome: EventOutcome;
    comment?: string;
    startDate?: string;
    startTime?: string;
  },
): Promise<void> {
  const { client, config } = await getHttpClient();
  try {
    if (config.mode === 'legacy') {
      const body = flattenFormData({
        outcome: payload.outcome,
        comment: payload.comment ?? '',
        startDate: payload.startDate ?? '',
        startTime: payload.startTime ?? '',
      });
      const { data } = await client.post<{ status?: boolean; message?: string }>(
        `/events/${id}/outcome`,
        body,
        { headers: { 'Content-Type': 'application/x-www-form-urlencoded' } },
      );
      if (data && data.status === false) {
        throw new Error(data.message || 'Enregistrement impossible');
      }
      return;
    }

    const eventStatus =
      payload.outcome === 'done' ? 3 : payload.outcome === 'canceled' ? 2 : 1;
    const patch: Record<string, unknown> = {
      event_status: eventStatus,
    };
    if (payload.outcome !== 'postponed') {
      patch.result_comment = payload.comment?.trim() || null;
    }
    if (payload.outcome === 'postponed' && payload.startDate) {
      const time = payload.startTime?.length === 5 ? payload.startTime : '09:00';
      patch.start_at = `${payload.startDate}T${time}:00`;
      const [hh, mm] = time.split(':').map(Number);
      const endH = Number.isFinite(hh) ? hh : 9;
      const endM = (Number.isFinite(mm) ? mm : 0) + 30;
      const endDate = new Date(`${payload.startDate}T${String(endH).padStart(2, '0')}:${String(endM % 60).padStart(2, '0')}:00`);
      if (!Number.isNaN(endDate.getTime())) {
        const pad = (n: number) => String(n).padStart(2, '0');
        patch.end_at = `${endDate.getFullYear()}-${pad(endDate.getMonth() + 1)}-${pad(endDate.getDate())}T${pad(endDate.getHours())}:${pad(endDate.getMinutes())}:00`;
      }
    }
    await client.patch(`/events/${id}`, patch, {
      headers: { 'Content-Type': 'application/json' },
    });
  } catch (err) {
    throw new Error(apiErrorMessage(err) || 'Enregistrement impossible');
  }
}
