import { config } from "./config.js";
import { createLogger } from "./logger.js";
import { getRuntimeSettings } from "./runtimeSettings.js";

const logger = createLogger(config.logLevel);

type PaperlessDocument = Record<string, unknown>;

interface PaperlessCollectionResponse<T> {
  results?: T[];
}

export class PaperlessClient {
  get isConfigured() {
    const { paperlessBaseUrl, paperlessToken } = getRuntimeSettings();
    return Boolean(paperlessBaseUrl && paperlessToken);
  }

  private buildUrl(path: string) {
    const { paperlessBaseUrl } = getRuntimeSettings();
    if (!paperlessBaseUrl) {
      throw new Error("Paperless base URL is not configured");
    }
    const trimmedBase = paperlessBaseUrl.replace(/\/+$/, "");
    const trimmedPath = path.replace(/^\/+/, "");
    return `${trimmedBase}/${trimmedPath}`;
  }

  private getHeaders(): HeadersInit {
    const headers: Record<string, string> = {
      Accept: "application/json"
    };
    const { paperlessToken } = getRuntimeSettings();
    if (paperlessToken) {
      headers.Authorization = `Token ${paperlessToken}`;
    }
    return headers;
  }

  private async fetchJson<T>(url: URL): Promise<T> {
    const response = await fetch(url, { headers: this.getHeaders() });
    if (!response.ok) {
      const text = await response.text();
      logger.error(`Paperless API error ${response.status}: ${text}`);
      throw new Error(`Paperless request failed with status ${response.status}`);
    }
    return response.json() as Promise<T>;
  }

  async getDocument(documentId: number): Promise<PaperlessDocument | null> {
    if (!this.isConfigured) {
      throw new Error("Paperless integration is not configured");
    }

    const url = new URL(this.buildUrl(`/api/documents/${documentId}/`));
    url.searchParams.set("metadata", "true");
    const response = await fetch(url, { headers: this.getHeaders() });

    if (response.status === 404) {
      return null;
    }

    if (!response.ok) {
      const text = await response.text();
      logger.error(`Paperless API error ${response.status}: ${text}`);
      throw new Error(`Paperless request failed with status ${response.status}`);
    }

    const document = (await response.json()) as PaperlessDocument;
    await this.enrichDocuments([document]);
    return document;
  }

  async searchDocuments(query: string, page = 1): Promise<Record<string, unknown>> {
    if (!this.isConfigured) {
      throw new Error("Paperless integration is not configured");
    }

    const url = new URL(this.buildUrl("/api/documents/"));
    url.searchParams.set("query", query);
    url.searchParams.set("page", page.toString());
    url.searchParams.set("metadata", "true");

    const payload = await this.fetchJson<Record<string, unknown>>(url);
    const results = Array.isArray((payload as PaperlessCollectionResponse<PaperlessDocument>).results)
      ? ((payload as PaperlessCollectionResponse<PaperlessDocument>).results as PaperlessDocument[])
      : [];
    await this.enrichDocuments(results);
    return payload;
  }

  async enrichDocuments(documents: PaperlessDocument[]): Promise<void> {
    if (!documents.length) return;

    const correspondentIds = new Set<number>();
    const tagIds = new Set<number>();

    for (const doc of documents) {
      if (typeof doc.correspondent === "number") {
        correspondentIds.add(doc.correspondent);
      }

      const tags = Array.isArray(doc.tags) ? doc.tags : [];
      for (const tag of tags) {
        if (typeof tag === "number") {
          tagIds.add(tag);
        }
      }
    }

    const [correspondents, tags] = await Promise.all([
      this.fetchCorrespondents(Array.from(correspondentIds)),
      this.fetchTags(Array.from(tagIds))
    ]);

    for (const doc of documents) {
      if (typeof doc.correspondent === "number" && correspondents.has(doc.correspondent)) {
        const correspondent = correspondents.get(doc.correspondent)!;
        doc.correspondent_name = correspondent.name ?? correspondent.slug ?? correspondent.title ?? correspondent.value ?? null;
        if (!doc.metadata || typeof doc.metadata !== "object") {
          doc.metadata = {};
        }
        (doc.metadata as Record<string, unknown>).correspondent_name = doc.correspondent_name;
      }

      if (Array.isArray(doc.tags) && doc.tags.length > 0) {
        const tagNames: string[] = [];
        const tagObjects: Array<Record<string, unknown>> = [];
        for (const tag of doc.tags) {
          if (typeof tag === "number" && tags.has(tag)) {
            const tagData = tags.get(tag)!;
            const name =
              tagData.name ??
              tagData.slug ??
              tagData.label ??
              tagData.title ??
              tagData.value ??
              null;
            if (name && typeof name === "string") {
              tagNames.push(name);
            }
            tagObjects.push(tagData);
          } else if (typeof tag === "string") {
            tagNames.push(tag);
          } else if (tag && typeof tag === "object") {
            tagObjects.push(tag as Record<string, unknown>);
          }
        }
        if (tagNames.length > 0) {
          doc.tags__name = tagNames;
          if (!doc.metadata || typeof doc.metadata !== "object") {
            doc.metadata = {};
          }
          (doc.metadata as Record<string, unknown>).tag_names = tagNames;
        }
        if (tagObjects.length > 0) {
          doc.tag_details = tagObjects;
        }
      }
    }
  }

  private async fetchCorrespondents(ids: number[]): Promise<Map<number, Record<string, unknown>>> {
    const map = new Map<number, Record<string, unknown>>();
    if (ids.length === 0) {
      return map;
    }

    const chunks = this.chunkIds(ids);
    for (const chunk of chunks) {
      const url = new URL(this.buildUrl("/api/correspondents/"));
      url.searchParams.set("id__in", chunk.join(","));
      url.searchParams.set("page_size", "100");
      const payload = await this.fetchJson<PaperlessCollectionResponse<Record<string, unknown>>>(url);
      const results = Array.isArray(payload.results) ? payload.results : [];
      for (const item of results) {
        if (typeof item.id === "number") {
          map.set(item.id, item);
        }
      }
    }

    return map;
  }

  private async fetchTags(ids: number[]): Promise<Map<number, Record<string, unknown>>> {
    const map = new Map<number, Record<string, unknown>>();
    if (ids.length === 0) {
      return map;
    }

    const chunks = this.chunkIds(ids);
    for (const chunk of chunks) {
      const url = new URL(this.buildUrl("/api/tags/"));
      url.searchParams.set("id__in", chunk.join(","));
      url.searchParams.set("page_size", "100");
      const payload = await this.fetchJson<PaperlessCollectionResponse<Record<string, unknown>>>(url);
      const results = Array.isArray(payload.results) ? payload.results : [];
      for (const item of results) {
        if (typeof item.id === "number") {
          map.set(item.id, item);
        }
      }
    }

    return map;
  }

  private chunkIds(ids: number[], size = 50): number[][] {
    const chunks: number[][] = [];
    for (let i = 0; i < ids.length; i += size) {
      chunks.push(ids.slice(i, i + size));
    }
    return chunks;
  }
}

export const paperlessClient = new PaperlessClient();
