import { mkdir, readFile, writeFile } from "node:fs/promises";
import path from "node:path";
import type { ParsedEventRow } from "@/lib/types";

type LocalImport = {
  id: number;
  uploaded_by: string;
  file_name: string;
  file_hash: string;
  storage_path: string | null;
  detected_country_code: string | null;
  status: "processing" | "completed" | "failed";
  total_rows: number;
  inserted_rows: number;
  updated_rows: number;
  failed_rows: number;
  error_summary: string | null;
  created_at: string;
};

type LocalRawEvent = {
  id: number;
  sent_email_name: string;
  primary_country_code: string;
  approved_document_name: string;
  last_open: string | null;
  opened: number;
  total_opens: number;
  last_click: string | null;
  clicked: number;
  total_clicks: number;
  sent_date: string | null;
  latest_import_id: number | null;
  created_at: string;
  updated_at: string;
};

type LocalEmailMetadata = {
  approved_document_name: string;
  friendly_title: string | null;
  brand: string | null;
  notes: string | null;
  thumbnail_path: string | null;
  campaign_group: string | null;
  updated_at: string;
};

type LocalEmailEventMetadataOverride = {
  sent_email_name: string;
  friendly_title: string | null;
  brand: string | null;
  notes: string | null;
  thumbnail_path: string | null;
  campaign_group: string | null;
  updated_at: string;
};

type LocalEmailSentiment = {
  sent_email_name: string;
  thumbs_up: number;
  thumbs_down: number;
  updated_at: string;
};


export type LocalDb = {
  version: number;
  imports: LocalImport[];
  raw_email_events: LocalRawEvent[];
  email_metadata: LocalEmailMetadata[];
  email_event_metadata_overrides: LocalEmailEventMetadataOverride[];
  email_sentiment: LocalEmailSentiment[];
};

const DATA_DIR = path.join(process.cwd(), "data");
const DB_PATH = path.join(DATA_DIR, "local-db.json");
const UPLOADS_DIR = path.join(DATA_DIR, "uploads");
const LOCAL_THUMBNAILS_DIR = path.join(process.cwd(), "public", "local-thumbnails");

const EMPTY_DB: LocalDb = {
  version: 1,
  imports: [],
  raw_email_events: [],
  email_metadata: [],
  email_event_metadata_overrides: [],
  email_sentiment: [],
};

async function ensureDataDir() {
  await mkdir(DATA_DIR, { recursive: true });
  await mkdir(UPLOADS_DIR, { recursive: true });
  await mkdir(LOCAL_THUMBNAILS_DIR, { recursive: true });
}

export async function getLocalDb() {
  await ensureDataDir();

  try {
    const raw = await readFile(DB_PATH, "utf8");
    const parsed = JSON.parse(raw) as Partial<LocalDb>;

    return {
      version: parsed.version ?? 1,
      imports: parsed.imports ?? [],
      raw_email_events: parsed.raw_email_events ?? [],
      email_metadata: parsed.email_metadata ?? [],
      email_event_metadata_overrides: parsed.email_event_metadata_overrides ?? [],
      email_sentiment: parsed.email_sentiment ?? [],
    };
  } catch {
    return { ...EMPTY_DB };
  }
}

export async function saveLocalDb(db: LocalDb) {
  await ensureDataDir();
  await writeFile(DB_PATH, JSON.stringify(db, null, 2), "utf8");
}

export async function saveLocalUpload(importId: number, fileName: string, bytes: Uint8Array) {
  await ensureDataDir();
  const safeName = fileName.replace(/[^a-zA-Z0-9._-]/g, "_");
  const relativePath = `uploads/${importId}-${safeName}`;
  const absolutePath = path.join(UPLOADS_DIR, `${importId}-${safeName}`);
  await writeFile(absolutePath, bytes);
  return relativePath;
}

export async function saveLocalThumbnail(sentEmailName: string, fileName: string, bytes: Uint8Array) {
  await ensureDataDir();
  const safeEmailName = sentEmailName.replace(/[^a-zA-Z0-9_-]/g, "_");
  const safeFileName = fileName.replace(/[^a-zA-Z0-9._-]/g, "_");
  const relativePublicPath = `local-thumbnails/${safeEmailName}-${Date.now()}-${safeFileName}`;
  const absolutePath = path.join(process.cwd(), "public", relativePublicPath);
  await writeFile(absolutePath, bytes);
  return relativePublicPath;
}

export function nextImportId(db: LocalDb) {
  return db.imports.reduce((max, item) => Math.max(max, item.id), 0) + 1;
}

export function pushProcessingImport(
  db: LocalDb,
  input: {
    id: number;
    uploadedBy: string;
    fileName: string;
    fileHash: string;
    storagePath: string | null;
  },
) {
  db.imports.unshift({
    id: input.id,
    uploaded_by: input.uploadedBy,
    file_name: input.fileName,
    file_hash: input.fileHash,
    storage_path: input.storagePath,
    detected_country_code: null,
    status: "processing",
    total_rows: 0,
    inserted_rows: 0,
    updated_rows: 0,
    failed_rows: 0,
    error_summary: null,
    created_at: new Date().toISOString(),
  });
}

export function updateImport(db: LocalDb, importId: number, patch: Partial<LocalImport>) {
  const row = db.imports.find((item) => item.id === importId);
  if (!row) return;
  Object.assign(row, patch);
}

export function mergeIntoLocalRawEvents(
  db: LocalDb,
  rows: ParsedEventRow[],
  importId: number,
): { insertedRows: number; updatedRows: number; failedRows: number } {
  let insertedRows = 0;
  let updatedRows = 0;
  let failedRows = 0;
  const bySentEmailName = new Map(db.raw_email_events.map((item) => [item.sent_email_name, item]));
  let nextId = db.raw_email_events.reduce((max, item) => Math.max(max, item.id), 0) + 1;

  for (const row of rows) {
    const sentEmailName = row.sent_email_name.trim();
    if (!sentEmailName) {
      failedRows += 1;
      continue;
    }

    const existing = bySentEmailName.get(sentEmailName);

    if (!existing) {
      const now = new Date().toISOString();

      const inserted = {
        id: nextId,
        sent_email_name: sentEmailName,
        primary_country_code: row.primary_country_code,
        approved_document_name: row.approved_document_name,
        last_open: row.last_open,
        opened: row.opened,
        total_opens: row.total_opens,
        last_click: row.last_click,
        clicked: row.clicked,
        total_clicks: row.total_clicks,
        sent_date: row.sent_date,
        latest_import_id: importId,
        created_at: now,
        updated_at: now,
      };

      db.raw_email_events.push(inserted);
      bySentEmailName.set(sentEmailName, inserted);
      nextId += 1;
      insertedRows += 1;
      continue;
    }

    // Re-uploads should win on conflicts, while still preserving existing values if incoming is blank.
    existing.primary_country_code = row.primary_country_code || existing.primary_country_code;
    existing.approved_document_name = row.approved_document_name || existing.approved_document_name;
    existing.last_open = row.last_open ?? existing.last_open;
    existing.opened = row.opened;
    existing.total_opens = row.total_opens;
    existing.last_click = row.last_click ?? existing.last_click;
    existing.clicked = row.clicked;
    existing.total_clicks = row.total_clicks;
    existing.sent_date = row.sent_date ?? existing.sent_date;
    existing.latest_import_id = importId;
    existing.updated_at = new Date().toISOString();
    updatedRows += 1;
  }

  return { insertedRows, updatedRows, failedRows };
}

export function upsertLocalDocumentBrands(db: LocalDb, documentBrands: Record<string, string>) {
  const now = new Date().toISOString();
  let updated = 0;
  let inserted = 0;
  const byApprovedDocumentName = new Map(
    db.email_metadata.map((item) => [item.approved_document_name, item]),
  );

  for (const [approvedDocumentNameRaw, brandRaw] of Object.entries(documentBrands)) {
    const approvedDocumentName = approvedDocumentNameRaw.trim();
    const brand = brandRaw.trim();
    if (!approvedDocumentName || !brand) continue;

    const existing = byApprovedDocumentName.get(approvedDocumentName);

    if (existing) {
      existing.brand = brand;
      existing.updated_at = now;
      updated += 1;
      continue;
    }

    const insertedMetadata: LocalEmailMetadata = {
      approved_document_name: approvedDocumentName,
      friendly_title: null,
      brand,
      notes: null,
      thumbnail_path: null,
      campaign_group: null,
      updated_at: now,
    };
    db.email_metadata.push(insertedMetadata);
    byApprovedDocumentName.set(approvedDocumentName, insertedMetadata);
    inserted += 1;
  }

  return { inserted, updated };
}

export function upsertLocalEmailSentiment(
  db: LocalDb,
  sentimentByEmail: Record<string, { thumbs_up: number; thumbs_down: number }>,
) {
  const now = new Date().toISOString();
  let inserted = 0;
  let updated = 0;
  const bySentEmailName = new Map(db.email_sentiment.map((item) => [item.sent_email_name, item]));

  for (const [sentEmailNameRaw, counts] of Object.entries(sentimentByEmail)) {
    const sentEmailName = sentEmailNameRaw.trim();
    if (!sentEmailName) continue;

    const thumbsUp = Math.max(0, Math.round(Number(counts.thumbs_up ?? 0)));
    const thumbsDown = Math.max(0, Math.round(Number(counts.thumbs_down ?? 0)));

    const existing = bySentEmailName.get(sentEmailName);
    if (existing) {
      existing.thumbs_up = thumbsUp;
      existing.thumbs_down = thumbsDown;
      existing.updated_at = now;
      updated += 1;
      continue;
    }

    const insertedRow: LocalEmailSentiment = {
      sent_email_name: sentEmailName,
      thumbs_up: thumbsUp,
      thumbs_down: thumbsDown,
      updated_at: now,
    };

    db.email_sentiment.push(insertedRow);
    bySentEmailName.set(sentEmailName, insertedRow);
    inserted += 1;
  }

  return { inserted, updated };
}

export function upsertLocalDocumentMetadata(
  db: LocalDb,
  input: {
    approved_document_name: string;
    friendly_title: string | null;
    brand: string | null;
    notes: string | null;
    thumbnail_path?: string | null;
    campaign_group: string | null;
  },
) {
  const approvedDocumentName = input.approved_document_name.trim();
  if (!approvedDocumentName) {
    throw new Error("approved_document_name is required");
  }

  const existing = db.email_metadata.find(
    (item) => item.approved_document_name === approvedDocumentName,
  );
  const now = new Date().toISOString();

  if (existing) {
    existing.friendly_title = input.friendly_title;
    existing.brand = input.brand;
    existing.notes = input.notes;
    existing.campaign_group = input.campaign_group;
    if (input.thumbnail_path !== undefined) {
      existing.thumbnail_path = input.thumbnail_path;
    }
    existing.updated_at = now;
    return;
  }

  db.email_metadata.push({
    approved_document_name: approvedDocumentName,
    friendly_title: input.friendly_title,
    brand: input.brand,
    notes: input.notes,
    thumbnail_path: input.thumbnail_path ?? null,
    campaign_group: input.campaign_group,
    updated_at: now,
  });
}

export function upsertLocalEventMetadataOverride(
  db: LocalDb,
  input: {
    sent_email_name: string;
    friendly_title: string | null;
    brand: string | null;
    notes: string | null;
    thumbnail_path?: string | null;
    campaign_group: string | null;
  },
) {
  const sentEmailName = input.sent_email_name.trim();
  if (!sentEmailName) {
    throw new Error("sent_email_name is required");
  }

  const existing = db.email_event_metadata_overrides.find(
    (item) => item.sent_email_name === sentEmailName,
  );
  const now = new Date().toISOString();

  if (existing) {
    existing.friendly_title = input.friendly_title;
    existing.brand = input.brand;
    existing.notes = input.notes;
    existing.campaign_group = input.campaign_group;
    if (input.thumbnail_path !== undefined) {
      existing.thumbnail_path = input.thumbnail_path;
    }
    existing.updated_at = now;
    return;
  }

  db.email_event_metadata_overrides.push({
    sent_email_name: sentEmailName,
    friendly_title: input.friendly_title,
    brand: input.brand,
    notes: input.notes,
    thumbnail_path: input.thumbnail_path ?? null,
    campaign_group: input.campaign_group,
    updated_at: now,
  });
}
