import { fetchApi, API_BASE_URL } from './http';
import type {
  ReportListParams,
  ReportListResponse,
  ReportByAuditItem,
  ReportContext,
  ReportEditorModel,
  CreateReportPayload,
  UpdateHeaderPayload,
  ClauseSaveItem,
  RaiseNcPayload,
  ReportAttendee,
  ReportStatus,
  FindingStatus,
} from './types/audit-report-builder.types';

const BASE = `${API_BASE_URL}/audit-reports`;

const json = (method: string, body?: unknown): RequestInit => ({
  method,
  body: body === undefined ? undefined : JSON.stringify(body),
});

// ── lists / context ─────────────────────────────────────────────
export function listReports(p: ReportListParams = {}): Promise<ReportListResponse> {
  const qs = new URLSearchParams();
  Object.entries(p).forEach(([k, v]) => {
    if (v !== undefined && v !== null && v !== '' && v !== 'all') qs.set(k, String(v));
  });
  const q = qs.toString();
  return fetchApi<ReportListResponse>(`${BASE}${q ? `?${q}` : ''}`);
}

export const getReportsByAudit = (rowId: number) =>
  fetchApi<ReportByAuditItem[]>(`${BASE}/by-audit/${rowId}`);

export const getReportContext = (rowId: number) =>
  fetchApi<ReportContext>(`${BASE}/context/${rowId}`);

// ── create / read ───────────────────────────────────────────────
export const createReport = (dto: CreateReportPayload) =>
  fetchApi<ReportEditorModel>(BASE, json('POST', dto));

export const getReport = (id: number) => fetchApi<ReportEditorModel>(`${BASE}/${id}`);

// ── edit ────────────────────────────────────────────────────────
export const updateReportHeader = (id: number, dto: UpdateHeaderPayload) =>
  fetchApi<{ ok: boolean }>(`${BASE}/${id}/header`, json('PATCH', dto));

export const saveClausesBulk = (id: number, items: ClauseSaveItem[]) =>
  fetchApi<any>(`${BASE}/${id}/clauses`, json('PUT', { items }));

export const raiseClauseNc = (id: number, clauseId: number, dto: RaiseNcPayload) =>
  fetchApi<{ ok: boolean; nc_id: number; ncr_entry_id: number }>(
    `${BASE}/${id}/clauses/${clauseId}/raise-nc`,
    json('POST', dto),
  );

export const saveSection = (id: number, key: string, body: string) =>
  fetchApi<any>(`${BASE}/${id}/sections/${encodeURIComponent(key)}`, json('PUT', { body }));

export const saveAttendance = (id: number, attendees: ReportAttendee[]) =>
  fetchApi<any>(
    `${BASE}/${id}/attendance`,
    json('PUT', {
      attendees: attendees.map((a) => ({
        name: a.name,
        designation: a.designation || undefined,
        signature_path: a.signature_path || undefined,
      })),
    }),
  );

export const refreshReportFromCrm = (id: number) =>
  fetchApi<any>(`${BASE}/${id}/refresh`, json('POST'));

// ── workflow ────────────────────────────────────────────────────
type WfRes = { ok: boolean; status: ReportStatus; version?: number };
/** Submit returns { message, problems: [...] } when the report is incomplete; show every problem, not just the headline. */
export async function submitReport(id: number): Promise<WfRes> {
  const t = token();
  const res = await fetch(`${BASE}/${id}/submit`, {
    method: 'POST',
    credentials: 'include',
    headers: { 'Content-Type': 'application/json', ...(t ? { Authorization: `Bearer ${t}` } : {}) },
  });
  const text = await res.text();
  let body: any = null;
  try { body = JSON.parse(text); } catch { /* not json */ }
  if (!res.ok) {
    const head = Array.isArray(body?.message) ? body.message.join(', ') : body?.message || text || `Request failed (${res.status})`;
    const problems: string[] = Array.isArray(body?.problems) ? body.problems : [];
    throw new Error([head, ...problems.map((x) => `• ${x}`)].join('\n'));
  }
  return body as WfRes;
}
export const approveReport = (id: number) => fetchApi<WfRes>(`${BASE}/${id}/approve`, json('POST'));
export const lockReport = (id: number) => fetchApi<WfRes>(`${BASE}/${id}/lock`, json('POST'));
export const returnReport = (id: number, reason: string) =>
  fetchApi<WfRes>(`${BASE}/${id}/return`, json('POST', { reason }));
export const reopenReport = (id: number, reason: string) =>
  fetchApi<WfRes>(`${BASE}/${id}/reopen`, json('POST', { reason }));

// ── preview / pdf (need the auth header, so fetch as blob) ──────
function token(): string | null {
  if (typeof window === 'undefined') return null;
  return localStorage.getItem('access_token') || sessionStorage.getItem('access_token');
}

async function fetchBlob(path: string): Promise<Blob> {
  const t = token();
  const res = await fetch(`${BASE}${path}`, {
    credentials: 'include',
    headers: t ? { Authorization: `Bearer ${t}` } : {},
  });
  if (!res.ok) {
    let msg = `Request failed (${res.status})`;
    try {
      const j = await res.json();
      msg = Array.isArray(j.message) ? j.message.join(', ') : j.message || msg;
    } catch {
      /* ignore */
    }
    throw new Error(msg);
  }
  return res.blob();
}

export const fetchReportPdfBlob = (id: number) => fetchBlob(`/${id}/pdf`);
export const fetchReportPreviewBlob = (id: number) => fetchBlob(`/${id}/preview`);


export async function openReportPdf(id: number): Promise<void> {
  const blob = await fetchReportPdfBlob(id);
  window.open(URL.createObjectURL(blob), '_blank', 'noopener');
}

export async function downloadReportPdf(id: number, name: string): Promise<void> {
  const blob = await fetchReportPdfBlob(id);
  const url = URL.createObjectURL(blob);
  const a = document.createElement('a');
  a.href = url;
  a.download = `${name}.pdf`;
  a.click();
  setTimeout(() => URL.revokeObjectURL(url), 5000);
}

// ── Word download ───────────────────────────────────────────────
export async function downloadReportDocx(id: number, name: string): Promise<void> {
  const blob = await fetchBlob(`/${id}/docx`);
  const url = URL.createObjectURL(blob);
  const a = document.createElement('a');
  a.href = url;
  a.download = `${name}.docx`;
  a.click();
  setTimeout(() => URL.revokeObjectURL(url), 5000);
}

// ── AI drafting (saved straight to the report; the auditor edits afterwards) ──
export interface AiEvidenceResult {
  generated: number;
  requested?: number;
  skipped_findings: number;
  items: { id: number; evidence_text: string; status?: FindingStatus; docs_reviewed?: string[] }[];
}
export const aiGenerateEvidence = (
  id: number,
  body: { clause_ids?: number[]; overwrite?: boolean; mark_conforming?: boolean } = {},
) => fetchApi<AiEvidenceResult>(`${BASE}/${id}/ai/evidence`, json('POST', body));

export interface AiSectionsResult {
  generated: number;
  items: { key: string; body: string }[];
}
export const aiGenerateSections = (id: number, body: { keys?: string[]; overwrite?: boolean } = {}) =>
  fetchApi<AiSectionsResult>(`${BASE}/${id}/ai/sections`, json('POST', body));

export const aiGenerateFinding = (
  id: number,
  body: { clause_id: number; note?: string; nc_type?: 'Major' | 'Minor' | 'Observation' },
) => fetchApi<{ statement: string }>(`${BASE}/${id}/ai/finding`, json('POST', body));

// ── Photos (products / premises / evidence) ─────────────────────
export interface ReportImage {
  id: number;
  report_id: number;
  section_key: string;
  clause_id: number | null;
  original_name: string;
  mime: string;
  width: number;
  height: number;
  size_bytes: number;
  caption: string;
  sort_order: number;
}

export function listReportImages(id: number, q: { section_key?: string; clause_id?: number } = {}) {
  const qs = new URLSearchParams();
  if (q.section_key) qs.set('section_key', q.section_key);
  if (q.clause_id) qs.set('clause_id', String(q.clause_id));
  const s = qs.toString();
  return fetchApi<ReportImage[]>(`${BASE}/${id}/images${s ? `?${s}` : ''}`);
}

export async function uploadReportImages(
  id: number,
  files: File[],
  o: { section_key?: string; clause_id?: number; caption?: string } = {},
): Promise<ReportImage[]> {
  const fd = new FormData();
  files.forEach((f) => fd.append('files', f));
  if (o.section_key) fd.append('section_key', o.section_key);
  if (o.clause_id) fd.append('clause_id', String(o.clause_id));
  if (o.caption) fd.append('caption', o.caption);
  const t = token();
  const res = await fetch(`${BASE}/${id}/images`, {
    method: 'POST',
    credentials: 'include',
    headers: t ? { Authorization: `Bearer ${t}` } : {}, // the browser sets the multipart boundary itself
    body: fd,
  });
  const text = await res.text();
  let body: any = null;
  try { body = JSON.parse(text); } catch { /* not json */ }
  if (!res.ok) {
    throw new Error(Array.isArray(body?.message) ? body.message.join(', ') : body?.message || `Upload failed (${res.status})`);
  }
  return body as ReportImage[];
}

export const updateReportImage = (id: number, imageId: number, body: { caption?: string; section_key?: string }) =>
  fetchApi<ReportImage>(`${BASE}/${id}/images/${imageId}`, json('PATCH', body));
export const deleteReportImage = (id: number, imageId: number) =>
  fetchApi<{ ok: boolean }>(`${BASE}/${id}/images/${imageId}`, json('DELETE'));
export const reorderReportImages = (id: number, ids: number[]) =>
  fetchApi<{ ok: boolean }>(`${BASE}/${id}/images-order`, json('PUT', { ids }));
export const fetchReportImageBlob = (id: number, imageId: number) => fetchBlob(`/${id}/images/${imageId}/file`);