import axios from 'axios';
import { i18n } from '../i18n';
import { getHttpClient, apiErrorMessage } from './http';
import { fetchPropertiesPage, fetchPropertyDetail } from './properties';

export const DEFAULT_OFFER_MODEL_ID = 0;

export interface OfferEmailModel {
  id: number;
  name: string;
  subject?: string | null;
  isDefault?: boolean;
}

export interface OfferProjectOption {
  id: number;
  name: string;
}

export interface OfferFormOptions {
  emailModels: OfferEmailModel[];
  pdfModels: OfferEmailModel[];
  projects: OfferProjectOption[];
  defaultValidityDays: number;
  defaultTemplate?: { subject: string; body: string };
}

export interface OfferItemRow {
  propertyId: number | null;
  propertyTitle?: string;
  propertyReference?: string;
  projectId?: number | null;
  projectName?: string;
  originalPrice: string;
  price: string;
  surface?: string;
  surfaceTerrain?: string;
}

export interface OfferPreview {
  subject: string;
  body: string;
  whatsappBody?: string;
  whatsappPhone?: string | null;
}

export interface CreatedOffer {
  id: number;
  reference: string;
}

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

function resolveOfferPrice(p: {
  price?: number | null;
  surface?: number | null;
}): number {
  const direct = p.price != null ? Number(p.price) : 0;
  return direct > 0 ? direct : 0;
}

const DEFAULT_OFFER_TEMPLATE = {
  get subject() {
    return i18n.t('offer.defaultSubject');
  },
  get body() {
    return i18n.t('offer.defaultBody');
  },
};

function withDefaultModel(models: OfferEmailModel[]): OfferEmailModel[] {
  if (models.some((m) => m.id === DEFAULT_OFFER_MODEL_ID || m.isDefault)) {
    return models;
  }
  return [{ id: DEFAULT_OFFER_MODEL_ID, name: i18n.t('message.defaultModel'), isDefault: true }, ...models];
}

export async function fetchOfferFormOptions(): Promise<OfferFormOptions> {
  const { client, config } = await getHttpClient();
  try {
    const { data } = await client.get<{ data: OfferFormOptions }>('/mobile/offer-form-options');
    return {
      emailModels: withDefaultModel(data.data?.emailModels ?? []),
      pdfModels: data.data?.pdfModels ?? [],
      projects: data.data?.projects ?? [],
      defaultValidityDays: data.data?.defaultValidityDays ?? 7,
      defaultTemplate: data.data?.defaultTemplate ?? DEFAULT_OFFER_TEMPLATE,
    };
  } catch (err) {
    if (config.mode === 'new') {
      const [metaRes, projectsRes] = await Promise.all([
        client.get<{
          data: {
            emailModels: OfferEmailModel[];
            pdfModels?: OfferEmailModel[];
            defaultReservationsDay?: number;
          };
        }>('/reservations-meta/options'),
        client.get<{ data: { id: number; name: string }[] }>('/projects', {
          params: { per_page: 100 },
        }),
      ]);
      const projects = (projectsRes.data.data ?? []).map((p) => ({
        id: p.id,
        name: p.name,
      }));
      return {
        emailModels: withDefaultModel(metaRes.data.data?.emailModels ?? []),
        pdfModels: metaRes.data.data?.pdfModels ?? [],
        projects,
        defaultValidityDays: metaRes.data.data?.defaultReservationsDay ?? 7,
        defaultTemplate: DEFAULT_OFFER_TEMPLATE,
      };
    }
    if (isMissingEndpoint(err)) {
      const modelsRes = await client.get<OfferEmailModel[]>('/models/reservations/list').catch(() => ({
        data: [] as OfferEmailModel[],
      }));
      const propsPage = await fetchPropertiesPage(1, {}).catch(() => null);
      return {
        emailModels: withDefaultModel(Array.isArray(modelsRes.data) ? modelsRes.data : []),
        pdfModels: [],
        projects: propsPage?.filters.projects ?? [],
        defaultValidityDays: 7,
        defaultTemplate: DEFAULT_OFFER_TEMPLATE,
      };
    }
    throw err;
  }
}

export interface OfferPropertyOption {
  id: number;
  title: string;
  reference: string;
  price: number | null;
  currency?: string | null;
  projectName?: string | null;
  rooms?: number | null;
  floor?: string | number | null;
  surface?: number | null;
  typeId?: number | null;
  typeName?: string | null;
  blockId?: number | null;
  blockName?: string | null;
  statusName?: string | null;
  statusId?: number | null;
  /** Blocage picker: only disponibles are selectable. */
  selectable?: boolean;
}

export type OfferPropertySearchFilters = {
  availableOnly?: boolean;
  rooms?: number | null;
  floor?: number | null;
  typeId?: number | null;
  blockId?: number | null;
  /** 1-based page (default 1). */
  page?: number;
  perPage?: number;
};

export type OfferPropertySearchResult = {
  data: OfferPropertyOption[];
  types: { id: number; name: string }[];
  blocks: { id: number; name: string }[];
  currency: string;
  meta: {
    current_page: number;
    last_page: number;
    per_page: number;
    total: number;
  };
};

/**
 * CRM « Disponible » only — never treat empty / unknown / bloqué / vendu as available.
 * Offer / blocage / réservation must only target disponibles.
 */
export function isCrmDisponible(
  statusName?: string | null,
  statusId?: number | null,
  disponibleId?: number,
): boolean {
  if (disponibleId != null && statusId != null && statusId === disponibleId) return true;
  const n = (statusName ?? '').trim();
  if (!n) return false;
  if (/bloqu|blocked|vendu|sold|réserv|reserv|option|indispon|unavailable/i.test(n)) {
    return false;
  }
  return /dispon|available|libre/i.test(n);
}

function resolveDisponibleStatusId(
  statuses: { id: number; name: string }[] | undefined,
): number {
  const list = statuses ?? [];
  const exact = list.find((s) => /^disponibles?$/i.test(s.name.trim()));
  if (exact) return exact.id;
  const fuzzy = list.find((s) => /dispon|available|libre/i.test(s.name) && !/indispon/i.test(s.name));
  if (fuzzy) return fuzzy.id;
  return 2; // AdminBundle Constants::PROPERTY_STATUT_DISPONIBLE
}

function toOfferOption(p: {
  id: number;
  title: string;
  reference: string;
  price: number | null;
  currency?: string | null;
  projectName?: string | null;
  rooms?: number | null;
  floor?: string | number | null;
  surface?: number | null;
  typeId?: number | null;
  typeName?: string | null;
  blockId?: number | null;
  blockName?: string | null;
  statusId?: number | null;
  statusName?: string | null;
}): OfferPropertyOption {
  return {
    id: p.id,
    title: p.title,
    reference: p.reference,
    price: p.price,
    currency: p.currency ?? 'TND',
    projectName: p.projectName,
    rooms: p.rooms ?? null,
    floor: p.floor ?? null,
    surface: p.surface ?? null,
    typeId: p.typeId ?? null,
    typeName: p.typeName ?? null,
    blockId: p.blockId ?? null,
    blockName: p.blockName ?? null,
    statusName: p.statusName ?? null,
    statusId: p.statusId ?? null,
  };
}

export async function searchOfferProperties(
  q: string,
  projectId?: number | null,
  opts?: OfferPropertySearchFilters,
): Promise<OfferPropertySearchResult> {
  const pageNum = Math.max(1, opts?.page ?? 1);
  const perPage = Math.min(Math.max(opts?.perPage ?? 20, 1), 50);
  const base = {
    q: q.trim() || undefined,
    projectId: projectId ?? undefined,
    typeId: opts?.typeId ?? undefined,
    blockId: opts?.blockId ?? undefined,
    rooms: opts?.rooms ?? undefined,
    floor: opts?.floor ?? undefined,
    perPage,
  };

  const pack = (
    page: Awaited<ReturnType<typeof fetchPropertiesPage>>,
    rows: OfferPropertyOption[],
  ): OfferPropertySearchResult => ({
    data: rows,
    types: page.filters.types ?? [],
    blocks: page.filters.blocks ?? [],
    currency: page.filters.currency ?? 'TND',
    meta: page.meta,
  });

  if (!opts?.availableOnly) {
    const page = await fetchPropertiesPage(pageNum, base);
    return pack(page, page.data.map(toOfferOption));
  }

  // Prefer server available_only (CRM statut Disponible); keep statusId when known.
  const probe = await fetchPropertiesPage(1, { projectId: base.projectId, perPage: 1 });
  const disponibleId = resolveDisponibleStatusId(probe.filters.statuses);

  const page = await fetchPropertiesPage(pageNum, {
    ...base,
    statusId: disponibleId ?? undefined,
    availableOnly: true,
  });

  return pack(
    page,
    page.data
      .filter((p) => isCrmDisponible(p.statusName, p.statusId ?? null, disponibleId))
      .map(toOfferOption),
  );
}

/**
 * CRM blocage picker — same rule as offre: disponibles only.
 */
export async function searchBlockageProperties(
  q: string,
  projectId: number,
): Promise<OfferPropertyOption[]> {
  const res = await searchOfferProperties(q, projectId, {
    availableOnly: true,
    perPage: 80,
    page: 1,
  });
  return res.data.map((p) => ({ ...p, selectable: true }));
}

/** @deprecated Prefer searchOfferProperties().data — kept for callers expecting an array. */
export async function searchOfferPropertiesList(
  q: string,
  projectId?: number | null,
  opts?: OfferPropertySearchFilters,
): Promise<OfferPropertyOption[]> {
  const res = await searchOfferProperties(q, projectId, opts);
  return res.data;
}

export async function loadOfferItemFromProperty(propertyId: number): Promise<OfferItemRow> {
  const p = await fetchPropertyDetail(propertyId);
  const price = resolveOfferPrice(p);
  return {
    propertyId: p.id,
    propertyTitle: p.title,
    propertyReference: p.reference,
    projectId: p.projectId ?? null,
    projectName: p.projectName ?? undefined,
    originalPrice: price > 0 ? String(price) : '',
    price: price > 0 ? String(price) : '',
    surface: p.surface != null ? String(p.surface) : undefined,
  };
}

export async function verifyOfferModel(payload: Record<string, unknown>): Promise<OfferPreview> {
  const { client } = await getHttpClient();
  try {
    const { data } = await client.post<{ data: OfferPreview }>('/mobile/offers/verify-model', payload);
    return data.data;
  } catch (err) {
    if (isMissingEndpoint(err)) {
      const { data } = await client.post<{ data: OfferPreview }>('/reservations/verify-model', payload);
      return data.data;
    }
    throw new Error(apiErrorMessage(err));
  }
}

export async function createOffer(payload: {
  clientId: number;
  projectId?: number | null;
  reservationDate: string;
  validityDays: number;
  emailModelId?: number | null;
  reservationPdfModelId?: number | null;
  emailSubject: string;
  emailBody: string;
  sendViaEmail: boolean;
  /** CRM Baileys send — always false on mobile (local WhatsApp + PDF). */
  sendViaWhatsapp?: boolean;
  /** Mobile: open phone WhatsApp with PDF after create. */
  localWhatsapp?: boolean;
  offerItems: OfferItemRow[];
}): Promise<CreatedOffer> {
  const { client, config } = await getHttpClient();
  const total = payload.offerItems.reduce(
    (sum, row) => sum + (parseFloat(row.price.replace(/\s/g, '').replace(',', '.')) || 0),
    0,
  );
  const offerItems = payload.offerItems
    .filter((r) => r.propertyId)
    .map((row) => ({
      property_id: row.propertyId,
      price: row.price ? parseFloat(row.price.replace(/\s/g, '').replace(',', '.')) : null,
      original_price: row.originalPrice
        ? parseFloat(row.originalPrice.replace(/\s/g, '').replace(',', '.'))
        : null,
      surface: row.surface ? parseFloat(row.surface) : null,
      surface_terrain: row.surfaceTerrain ? parseFloat(row.surfaceTerrain) : null,
    }));

  if (config.mode === 'legacy') {
    const now = new Date();
    const pad = (n: number) => String(n).padStart(2, '0');
    const dateSignature = `${pad(now.getDate())}/${pad(now.getMonth() + 1)}/${now.getFullYear()} ${pad(now.getHours())}:${pad(now.getMinutes())}`;
    const body = {
      client: { clientId: payload.clientId },
      projet: payload.projectId ?? undefined,
      offerItems: offerItems.map((item) => ({
        property: item.property_id,
        price: item.price,
        surfaceTerrain: item.surface_terrain,
      })),
      dateSignature,
      emailModel:
        payload.emailModelId && payload.emailModelId > 0 ? payload.emailModelId : undefined,
      pdfModel:
        payload.reservationPdfModelId && payload.reservationPdfModelId > 0
          ? payload.reservationPdfModelId
          : undefined,
      reservation_pdf_model_id:
        payload.reservationPdfModelId && payload.reservationPdfModelId > 0
          ? payload.reservationPdfModelId
          : undefined,
      sendViaEmail: payload.sendViaEmail,
      // Local WhatsApp on device — do not send via CRM session
      sendViaWhatsapp: false,
      emailSubject: payload.emailSubject,
      emailBody: payload.emailBody,
    };
    try {
      const { data } = await client.post<{ status?: boolean; errors?: string; data?: CreatedOffer }>(
        '/mobile/offers',
        body,
      );
      if (data.status === false) {
        throw new Error(data.errors || 'Échec création offre');
      }
      return data.data ?? { id: 0, reference: 'OFF' };
    } catch (err) {
      if (isMissingEndpoint(err)) {
        const { data } = await client.post<{ status?: boolean; errors?: string }>(
          '/reservations/offer/create',
          body,
        );
        if (data.status === false) {
          throw new Error(data.errors || 'Échec création offre');
        }
        return { id: 0, reference: 'OFF' };
      }
      throw new Error(apiErrorMessage(err));
    }
  }

  const apiPayload = {
    kind: 'offer',
    client_id: payload.clientId,
    project_id: payload.projectId ?? null,
    reservation_date: payload.reservationDate,
    reservation_validity: payload.validityDays,
    email_model_id:
      payload.emailModelId && payload.emailModelId > 0 ? payload.emailModelId : null,
    reservation_pdf_model_id:
      payload.reservationPdfModelId && payload.reservationPdfModelId > 0
        ? payload.reservationPdfModelId
        : null,
    email_subject: payload.emailSubject,
    email_body: payload.emailBody,
    show_model_text: true,
    send_via_email: payload.sendViaEmail,
    send_via_whatsapp: false,
    local_whatsapp: payload.localWhatsapp ?? true,
    total_price: total,
    property_id: offerItems[0]?.property_id ?? null,
    offer_items: offerItems,
  };

  try {
    const { data } = await client.post<{ data: { id: number; reference: string } }>(
      '/mobile/offers',
      apiPayload,
    );
    return { id: data.data.id, reference: data.data.reference };
  } catch (err) {
    if (isMissingEndpoint(err)) {
      const { data } = await client.post<{ data: { id: number; reference: string } }>(
        '/reservations',
        apiPayload,
      );
      return { id: data.data.id, reference: data.data.reference };
    }
    throw new Error(apiErrorMessage(err));
  }
}

export { apiErrorMessage };
