import type {
  ActivityEvent,
  BankMatchCandidate,
  BankStats,
  BankTx,
  Client,
  ClientQuery,
  Invoice,
  InvoiceStatus,
  QueryStats,
  Settings,
  Suggestion,
} from "./ledger-types";
import type { AuthPlatform } from "./auth";

const API_BASE = import.meta.env.VITE_API_URL ?? "/api";
const USE_API = import.meta.env.VITE_USE_API !== "false";

export const isApiEnabled = () => USE_API;

async function request<T>(path: string, init?: RequestInit): Promise<T> {
  const response = await fetch(`${API_BASE}${path}`, {
    headers: {
      "Content-Type": "application/json",
      ...(init?.headers ?? {}),
    },
    ...init,
  });

  if (!response.ok) {
    const text = await response.text();
    let message = text || `Request failed: ${response.status}`;
    try {
      const data = JSON.parse(text) as { detail?: unknown };
      if (typeof data.detail === "string") message = data.detail;
      else if (Array.isArray(data.detail) && data.detail[0]?.msg) message = String(data.detail[0].msg);
    } catch {
      /* keep text */
    }
    const error = new Error(message) as Error & { status?: number };
    error.status = response.status;
    throw error;
  }

  if (response.status === 204) {
    return undefined as T;
  }

  return (await response.json()) as T;
}

export type FirmActor = {
  platform: AuthPlatform;
  email: string;
  actor?: string;
};

function firmBody(actor: FirmActor, extra: Record<string, unknown> = {}) {
  return {
    platform: actor.platform,
    email: actor.email.trim().toLowerCase(),
    actor: actor.actor ?? "",
    ...extra,
  };
}

export interface LedgerStatePayload {
  clients: Client[];
  invoices: Invoice[];
  transactions: BankTx[];
  queries: ClientQuery[];
  activity: ActivityEvent[];
  settings: Settings;
}

export async function checkApiHealth(): Promise<boolean> {
  try {
    const response = await fetch(`${API_BASE}/health`);
    return response.ok;
  } catch {
    return false;
  }
}

export function fetchLedgerState(platform: AuthPlatform, email: string) {
  const params = new URLSearchParams({
    platform,
    email: email.trim().toLowerCase(),
  });
  return request<LedgerStatePayload>(`/v1/state?${params.toString()}`);
}

export class DuplicateUploadError extends Error {
  existing: { id: string; file: string; uploadedAt: string; status: string };

  constructor(existing: { id: string; file: string; uploadedAt: string; status: string }) {
    super("Duplicate document detected");
    this.name = "DuplicateUploadError";
    this.existing = existing;
  }
}

export function uploadDocumentFileApi(
  file: File,
  options: {
    platform: AuthPlatform;
    email: string;
    clientId?: string;
    actor: string;
    forceDuplicate?: boolean;
    onProgress?: (percent: number) => void;
  },
): Promise<Invoice> {
  return new Promise((resolve, reject) => {
    const xhr = new XMLHttpRequest();
    const form = new FormData();
    form.append("file", file);
    form.append("platform", options.platform);
    form.append("email", options.email);
    form.append("actor", options.actor);
    if (options.clientId) form.append("clientId", options.clientId);
    if (options.forceDuplicate) form.append("forceDuplicate", "true");

    xhr.upload.addEventListener("progress", (event) => {
      if (event.lengthComputable && options.onProgress) {
        options.onProgress(Math.round((event.loaded / event.total) * 100));
      }
    });

    xhr.addEventListener("load", () => {
      let data: Record<string, unknown> = {};
      try {
        data = JSON.parse(xhr.responseText) as Record<string, unknown>;
      } catch {
        reject(new Error("We couldn't upload this document. Please try again."));
        return;
      }
      if (xhr.status >= 200 && xhr.status < 300) {
        resolve(data as Invoice);
        return;
      }
      if (xhr.status === 409 && data.detail === "duplicate" && data.existing) {
        reject(new DuplicateUploadError(data.existing as DuplicateUploadError["existing"]));
        return;
      }
      const detail = data.detail;
      reject(
        new Error(
          typeof detail === "string"
            ? detail
            : "We couldn't upload this document. Please try again.",
        ),
      );
    });

    xhr.addEventListener("error", () => {
      reject(new Error("We couldn't upload this document. Please try again."));
    });

    xhr.open("POST", `${API_BASE}/v1/documents/upload`);
    xhr.send(form);
  });
}

/** @deprecated Use uploadDocumentFileApi with a real File */
export function uploadDocumentApi(clientId: string, fileName: string, actor: string) {
  return request<Invoice>("/v1/documents/upload", {
    method: "POST",
    body: JSON.stringify({ clientId, fileName, actor }),
  });
}

export function fetchInvoicesApi(
  session: { platform: AuthPlatform; email: string },
  filters: {
    page?: number;
    pageSize?: number;
    search?: string;
    status?: string;
    clientId?: string;
    confidence?: string;
    date?: string;
  } = {},
) {
  const params = new URLSearchParams({
    platform: session.platform,
    email: session.email.trim().toLowerCase(),
    page: String(filters.page ?? 1),
    page_size: String(filters.pageSize ?? 20),
  });
  if (filters.search?.trim()) params.set("search", filters.search.trim());
  if (filters.status && filters.status !== "all") params.set("status", filters.status);
  if (filters.clientId && filters.clientId !== "all") params.set("client", filters.clientId);
  if (filters.confidence && filters.confidence !== "all") params.set("confidence", filters.confidence);
  if (filters.date) params.set("date", filters.date);
  return request<InvoiceListResponse>(`/v1/invoices?${params.toString()}`);
}

export interface InvoiceListItem extends Invoice {
  clientName?: string;
}

export interface InvoiceListResponse {
  items: InvoiceListItem[];
  page: number;
  page_size: number;
  total: number;
  total_pages: number;
  has_any: boolean;
}

export function updateInvoiceApi(id: string, patch: Partial<Invoice>, actor: FirmActor) {
  return request<Invoice>(`/v1/invoices/${id}`, {
    method: "PATCH",
    body: JSON.stringify(firmBody(actor, { patch })),
  });
}

export function updateSuggestionApi(id: string, patch: Partial<Suggestion>, actor: FirmActor) {
  return request<Invoice>(`/v1/invoices/${id}/suggestion`, {
    method: "PATCH",
    body: JSON.stringify(firmBody(actor, { patch, editedBy: actor.actor ?? "" })),
  });
}

export function setInvoiceStatusApi(id: string, status: InvoiceStatus, actor: FirmActor) {
  return request<Invoice>(`/v1/invoices/${id}/status`, {
    method: "POST",
    body: JSON.stringify(firmBody(actor, { status })),
  });
}

export function approveInvoiceApi(id: string, actor: FirmActor) {
  return request<Invoice>(`/v1/invoices/${id}/approve`, {
    method: "POST",
    body: JSON.stringify(firmBody(actor)),
  });
}

export function rejectInvoiceApi(id: string, reason: string, actor: FirmActor) {
  return request<Invoice>(`/v1/invoices/${id}/reject`, {
    method: "POST",
    body: JSON.stringify(firmBody(actor, { reason })),
  });
}

export function syncInvoiceApi(id: string, actor: FirmActor) {
  return request<Invoice>(`/v1/invoices/${id}/sync`, {
    method: "POST",
    body: JSON.stringify(firmBody(actor)),
  });
}

export function bulkApproveApi(ids: string[], actor: FirmActor) {
  return request<Invoice[]>("/v1/invoices/bulk/approve", {
    method: "POST",
    body: JSON.stringify(firmBody(actor, { ids })),
  });
}

export function bulkNeedsReviewApi(ids: string[], actor: FirmActor) {
  return request<Invoice[]>("/v1/invoices/bulk/needs-review", {
    method: "POST",
    body: JSON.stringify(firmBody(actor, { ids })),
  });
}

export function createQueryApi(
  clientId: string,
  question: string,
  actor: FirmActor,
  opts?: { invoiceId?: string; title?: string; priority?: string },
) {
  return request<ClientQuery>("/v1/queries", {
    method: "POST",
    body: JSON.stringify(
      firmBody(actor, {
        clientId,
        question,
        invoiceId: opts?.invoiceId,
        title: opts?.title,
        priority: opts?.priority ?? "Normal",
      }),
    ),
  });
}

export interface QueryListResponse {
  items: ClientQuery[];
  stats: QueryStats;
}

export function fetchQueriesApi(
  session: { platform: AuthPlatform; email: string },
  filters: {
    clientId?: string;
    status?: string;
    priority?: string;
    dateFrom?: string;
    dateTo?: string;
    invoiceId?: string;
    q?: string;
  } = {},
) {
  const params = new URLSearchParams({
    platform: session.platform,
    email: session.email.trim().toLowerCase(),
  });
  if (filters.clientId) params.set("clientId", filters.clientId);
  if (filters.status && filters.status !== "all") params.set("status", filters.status);
  if (filters.priority && filters.priority !== "all") params.set("priority", filters.priority);
  if (filters.dateFrom) params.set("dateFrom", filters.dateFrom);
  if (filters.dateTo) params.set("dateTo", filters.dateTo);
  if (filters.invoiceId) params.set("invoiceId", filters.invoiceId);
  if (filters.q) params.set("q", filters.q);
  return request<QueryListResponse>(`/v1/queries?${params.toString()}`);
}

export function fetchQueryApi(id: string, session: { platform: AuthPlatform; email: string }) {
  const params = new URLSearchParams({
    platform: session.platform,
    email: session.email.trim().toLowerCase(),
  });
  return request<ClientQuery>(`/v1/queries/${encodeURIComponent(id)}?${params.toString()}`);
}

export function addQueryMessageApi(
  id: string,
  message: string,
  session: { platform: AuthPlatform; email: string; actor?: string },
  attachmentRef?: string,
) {
  return request<ClientQuery>(`/v1/queries/${encodeURIComponent(id)}/messages`, {
    method: "POST",
    body: JSON.stringify({
      platform: session.platform,
      email: session.email.trim().toLowerCase(),
      actor: session.actor ?? "",
      message,
      attachmentRef,
    }),
  });
}

export function answerQueryApi(
  id: string,
  answer: string,
  proof: string | undefined,
  session: { platform: AuthPlatform; email: string },
) {
  return request<ClientQuery>(`/v1/queries/${encodeURIComponent(id)}/answer`, {
    method: "POST",
    body: JSON.stringify({
      platform: session.platform,
      email: session.email.trim().toLowerCase(),
      answer,
      proof,
    }),
  });
}

export function resolveQueryApi(id: string, actor: FirmActor) {
  return request<ClientQuery>(`/v1/queries/${encodeURIComponent(id)}/resolve`, {
    method: "POST",
    body: JSON.stringify(firmBody(actor)),
  });
}

export function cancelQueryApi(id: string, actor: FirmActor, reason?: string) {
  return request<ClientQuery>(`/v1/queries/${encodeURIComponent(id)}/cancel`, {
    method: "POST",
    body: JSON.stringify(firmBody(actor, { reason })),
  });
}

export function setDuplicateStatusApi(id: string, status: string, actor: FirmActor) {
  return request<Invoice>(`/v1/invoices/${id}/duplicate-status`, {
    method: "POST",
    body: JSON.stringify(firmBody(actor, { status })),
  });
}

export function approveQueryApi(id: string, actor: FirmActor) {
  return request<ClientQuery>(`/v1/queries/${encodeURIComponent(id)}/approve`, {
    method: "POST",
    body: JSON.stringify(firmBody(actor)),
  });
}

export function setTxStatusApi(id: string, status: BankTx["status"], actor: FirmActor) {
  return request<BankTx>(`/v1/transactions/${id}/status`, {
    method: "POST",
    body: JSON.stringify(firmBody(actor, { status })),
  });
}

export interface BankListResponse {
  items: BankTx[];
  stats: BankStats;
  snelstart: {
    integrationEnabled: boolean;
    anyConnected: boolean;
    connectedClients: number;
  };
}

export function fetchBankTransactionsApi(
  actor: FirmActor,
  filters: {
    clientId?: string;
    status?: string;
    dateFrom?: string;
    dateTo?: string;
    q?: string;
  } = {},
) {
  const params = new URLSearchParams({
    platform: actor.platform,
    email: actor.email.trim().toLowerCase(),
  });
  if (filters.clientId) params.set("clientId", filters.clientId);
  if (filters.status && filters.status !== "all") params.set("status", filters.status);
  if (filters.dateFrom) params.set("dateFrom", filters.dateFrom);
  if (filters.dateTo) params.set("dateTo", filters.dateTo);
  if (filters.q) params.set("q", filters.q);
  return request<BankListResponse>(`/v1/bank-transactions?${params.toString()}`);
}

export function fetchBankMatchesApi(txId: string, actor: FirmActor) {
  const params = new URLSearchParams({
    platform: actor.platform,
    email: actor.email.trim().toLowerCase(),
  });
  return request<{ transaction: BankTx; matches: BankMatchCandidate[] }>(
    `/v1/bank-transactions/${txId}/matches?${params.toString()}`,
  );
}

export function searchBankMatchesApi(txId: string, q: string, actor: FirmActor) {
  const params = new URLSearchParams({
    platform: actor.platform,
    email: actor.email.trim().toLowerCase(),
    q,
  });
  return request<{ items: BankMatchCandidate[] }>(
    `/v1/bank-transactions/${txId}/search-matches?${params.toString()}`,
  );
}

export function approveBankMatchApi(
  txId: string,
  invoiceId: string,
  actor: FirmActor,
  amount?: number,
) {
  return request<BankTx>(`/v1/bank-transactions/${txId}/match`, {
    method: "POST",
    body: JSON.stringify(firmBody(actor, { invoiceId, amount })),
  });
}

export function rejectBankMatchApi(txId: string, actor: FirmActor) {
  return request<BankTx>(`/v1/bank-transactions/${txId}/reject-match`, {
    method: "POST",
    body: JSON.stringify(firmBody(actor)),
  });
}

export function ignoreBankTransactionApi(txId: string, reason: string, actor: FirmActor) {
  return request<BankTx>(`/v1/bank-transactions/${txId}/ignore`, {
    method: "POST",
    body: JSON.stringify(firmBody(actor, { reason })),
  });
}

export function syncBankTransactionsApi(actor: FirmActor) {
  return request<{
    imported: number;
    clientsChecked: number;
    connected: boolean;
    connectedClients: number;
    integrationEnabled: boolean;
    message: string;
    stats: BankStats;
  }>("/v1/bank-transactions/sync", {
    method: "POST",
    body: JSON.stringify(firmBody(actor)),
  });
}

export function updateSettingsApi(patch: Partial<Settings>) {
  return request<Settings>("/v1/settings", {
    method: "PATCH",
    body: JSON.stringify({ patch }),
  });
}

export function setClientRuleApi(clientId: string, rule: string) {
  return request<Settings>(`/v1/settings/client-rules/${clientId}`, {
    method: "PATCH",
    body: JSON.stringify({ rule }),
  });
}

export interface CreatedClient extends Client {
  portal?: {
    name: string;
    email: string;
    company?: string | null;
    status?: string;
  };
}

export function createClientApi(
  payload: {
    name: string;
    companyName?: string;
    email: string;
    password: string;
    kvk?: string;
  },
  actor: FirmActor,
) {
  const params = new URLSearchParams({
    platform: actor.platform,
    email: actor.email.trim().toLowerCase(),
  });
  return request<CreatedClient>(`/v1/clients?${params.toString()}`, {
    method: "POST",
    body: JSON.stringify({
      name: payload.name,
      company_name: payload.companyName ?? "",
      email: payload.email.trim().toLowerCase(),
      password: payload.password,
      kvk: payload.kvk ?? "",
    }),
  });
}

export interface ClientListItem extends Client {
  documentCount: number;
  openDocuments: number;
  openQueries: number;
  lastActivityAt: number | null;
}

export interface ClientListResponse {
  items: ClientListItem[];
  page: number;
  page_size: number;
  total: number;
  total_pages: number;
}

export function fetchClientsApi(
  session: { platform: AuthPlatform; email: string },
  filters: {
    page?: number;
    pageSize?: number;
    search?: string;
    connected?: boolean;
  } = {},
) {
  const params = new URLSearchParams({
    platform: session.platform,
    email: session.email.trim().toLowerCase(),
    page: String(filters.page ?? 1),
    page_size: String(filters.pageSize ?? 20),
  });
  if (filters.search?.trim()) params.set("search", filters.search.trim());
  if (filters.connected) params.set("connected", "true");
  return request<ClientListResponse>(`/v1/clients?${params.toString()}`);
}

export interface ClientOverviewDocument {
  id: string;
  file: string;
  clientId: string;
  supplier: string;
  number: string;
  total: number;
  currency: string;
  uploadedAt: string;
  status: Invoice["status"] | string;
  confidence: number;
  duplicateStatus: string;
  invoiceDate: string;
}

export interface ClientOverviewQuery {
  id: string;
  clientId: string;
  invoiceId?: string | null;
  invoiceLabel?: string | null;
  title?: string | null;
  question: string;
  status: string;
  priority: string;
  createdAt: string;
  updatedAt?: string | null;
  lastActivityAt?: string | null;
}

export interface ClientOverviewResponse {
  client: Client;
  portal: {
    name: string;
    email: string;
    company?: string | null;
    avatarUrl?: string | null;
    createdAt?: string | null;
  } | null;
  snelstart: {
    clientId: string;
    status: string;
    externalAdministrationId?: string | null;
    lastSyncAt?: string | null;
    lastSyncError?: string | null;
    integrationEnabled: boolean;
    isConnected: boolean;
  };
  kpis: {
    documents: number;
    needsReview: number;
    openQueries: number;
    matchedTransactions: number;
  };
  lastActivity: { at: number; text: string; type: string } | null;
  documents: ClientOverviewDocument[];
  queries: ClientOverviewQuery[];
  activity: ActivityEvent[];
  bank: BankStats;
}

export function fetchClientOverviewApi(clientId: string, actor: Pick<FirmActor, "platform" | "email">) {
  const params = new URLSearchParams({
    platform: actor.platform,
    email: actor.email.trim().toLowerCase(),
  });
  return request<ClientOverviewResponse>(
    `/v1/clients/${encodeURIComponent(clientId)}?${params.toString()}`,
  );
}

export function fetchFinancialReportsApi(
  session: { platform: AuthPlatform; email: string },
  clientId?: string,
) {
  const params = new URLSearchParams({
    platform: session.platform,
    email: session.email.trim().toLowerCase(),
  });
  if (clientId) params.set("clientId", clientId);
  return request<{ items: import("@/lib/ledger-types").FinancialReport[]; integrationRequired: boolean }>(
    `/v1/financial-reports?${params}`,
  );
}

export function createFinancialReportApi(clientId: string, period: string, actor: FirmActor) {
  return request<import("@/lib/ledger-types").FinancialReport>("/v1/financial-reports", {
    method: "POST",
    body: JSON.stringify(firmBody(actor, { clientId, period })),
  });
}

export function runUploadRemindersApi(actor: FirmActor, dryRun = false) {
  return request<{ checked: number; sent: number; skipped: number }>("/v1/jobs/upload-reminders", {
    method: "POST",
    body: JSON.stringify(firmBody(actor, { dryRun })),
  });
}

export interface SnelStartConnectionItem {
  clientId: string;
  clientName?: string;
  status: string;
  externalAdministrationId: string | null;
  lastSyncAt: string | null;
  lastSyncError: string | null;
  integrationEnabled: boolean;
  isConnected: boolean;
}

export interface SnelStartConnectionsResponse {
  live: boolean;
  integrationEnabled: boolean;
  configured: {
    apiBaseUrl: boolean;
    clientId: boolean;
    clientSecret: boolean;
    redirectUri: boolean;
  };
  apiHost: string | null;
  probe: {
    checked: boolean;
    reachable: boolean | null;
    httpStatus: number | null;
    message: string;
  };
  counts: {
    clients: number;
    connected: number;
    pending: number;
    error: number;
    notConnected: number;
  };
  items: SnelStartConnectionItem[];
  checkedAt: string;
}

export function fetchSnelStartConnectionsApi(actor: { platform: AuthPlatform; email: string }) {
  const params = new URLSearchParams({
    platform: actor.platform,
    email: actor.email.trim().toLowerCase(),
  });
  return request<SnelStartConnectionsResponse>(`/v1/snelstart/connections?${params.toString()}`);
}
