import { eq, and, gte, lte, isNull, or, ilike } from 'drizzle-orm';

import { db } from '@/lib/db';
import { expenses } from '@/lib/db/schema/financial';
import { generatePdf } from '@/lib/pdf/generate';
import { getRate } from '@/lib/services/currency';
import { logger } from '@/lib/logger';
import type { ListExpensesInput } from '@/lib/validators/expenses';

async function fetchAllExpenses(portId: string, query: ListExpensesInput) {
  const conditions: ReturnType<typeof eq>[] = [eq(expenses.portId, portId) as ReturnType<typeof eq>];

  if (!query.includeArchived) {
    conditions.push(isNull(expenses.archivedAt) as unknown as ReturnType<typeof eq>);
  }
  if (query.category) {
    conditions.push(eq(expenses.category, query.category) as ReturnType<typeof eq>);
  }
  if (query.paymentStatus) {
    conditions.push(eq(expenses.paymentStatus, query.paymentStatus) as ReturnType<typeof eq>);
  }
  if (query.currency) {
    conditions.push(eq(expenses.currency, query.currency) as ReturnType<typeof eq>);
  }
  if (query.payer) {
    conditions.push(eq(expenses.payer, query.payer) as ReturnType<typeof eq>);
  }
  if (query.dateFrom) {
    conditions.push(gte(expenses.expenseDate, new Date(query.dateFrom)) as unknown as ReturnType<typeof eq>);
  }
  if (query.dateTo) {
    conditions.push(lte(expenses.expenseDate, new Date(query.dateTo)) as unknown as ReturnType<typeof eq>);
  }
  if (query.search) {
    conditions.push(
      or(
        ilike(expenses.establishmentName, `%${query.search}%`),
        ilike(expenses.description, `%${query.search}%`),
      ) as unknown as ReturnType<typeof eq>,
    );
  }

  return db
    .select()
    .from(expenses)
    .where(and(...conditions));
}

export async function exportCsv(portId: string, query: ListExpensesInput): Promise<string> {
  const rows = await fetchAllExpenses(portId, query);

  const headers = [
    'Date',
    'Establishment',
    'Category',
    'Amount',
    'Currency',
    'Amount USD',
    'Payment Status',
    'Payment Method',
    'Description',
  ];

  const csvRows = rows.map((r) => {
    const date = r.expenseDate ? new Date(r.expenseDate).toISOString().split('T')[0] : '';
    return [
      date,
      r.establishmentName ?? '',
      r.category ?? '',
      r.amount,
      r.currency,
      r.amountUsd ?? 'N/A',
      r.paymentStatus ?? '',
      r.paymentMethod ?? '',
      (r.description ?? '').replace(/"/g, '""'),
    ]
      .map((v) => `"${v}"`)
      .join(',');
  });

  return [headers.join(','), ...csvRows].join('\n');
}

export async function exportPdf(portId: string, query: ListExpensesInput): Promise<Uint8Array> {
  const rows = await fetchAllExpenses(portId, query);

  const template = {
    basePdf: { width: 210, height: 297, padding: [10, 10, 10, 10] },
    schemas: [
      [
        {
          name: 'title',
          type: 'text',
          position: { x: 10, y: 10 },
          width: 190,
          height: 10,
          fontSize: 14,
          fontColor: '#000000',
        },
        {
          name: 'content',
          type: 'text',
          position: { x: 10, y: 25 },
          width: 190,
          height: 260,
          fontSize: 8,
          fontColor: '#000000',
        },
      ],
    ],
  };

  const lines = rows.map((r) => {
    const date = r.expenseDate ? new Date(r.expenseDate).toISOString().split('T')[0] : '';
    return `${date} | ${r.establishmentName ?? '-'} | ${r.category ?? '-'} | ${r.amount} ${r.currency} | ${r.paymentStatus ?? '-'}`;
  });

  const inputs = [
    {
      title: 'Expense Report',
      content: lines.join('\n'),
    },
  ];

  return generatePdf(template as unknown as Parameters<typeof generatePdf>[0], inputs);
}

export async function exportParentCompany(
  portId: string,
  query: ListExpensesInput,
): Promise<Uint8Array> {
  // BR-043: Convert all amounts to EUR, add 5% management fee
  const rows = await fetchAllExpenses(portId, query);
  const eurRate = await getRate('USD', 'EUR');

  if (!eurRate) {
    logger.warn('EUR rate unavailable for parent company export, using 1:1 fallback');
  }

  const rate = eurRate ?? 1;

  const convertedRows = rows.map((r) => {
    const amountUsd = r.amountUsd ? Number(r.amountUsd) : Number(r.amount);
    const amountEur = Number((amountUsd * rate).toFixed(2));
    return {
      date: r.expenseDate ? new Date(r.expenseDate).toISOString().split('T')[0] : '',
      establishment: r.establishmentName ?? '-',
      category: r.category ?? '-',
      amountEur,
    };
  });

  const subtotal = convertedRows.reduce((sum, r) => sum + r.amountEur, 0);
  const fee = Number((subtotal * 0.05).toFixed(2));
  const total = Number((subtotal + fee).toFixed(2));

  const template = {
    basePdf: { width: 210, height: 297, padding: [10, 10, 10, 10] },
    schemas: [
      [
        {
          name: 'title',
          type: 'text',
          position: { x: 10, y: 10 },
          width: 190,
          height: 10,
          fontSize: 14,
          fontColor: '#000000',
        },
        {
          name: 'content',
          type: 'text',
          position: { x: 10, y: 25 },
          width: 190,
          height: 230,
          fontSize: 8,
          fontColor: '#000000',
        },
        {
          name: 'summary',
          type: 'text',
          position: { x: 10, y: 260 },
          width: 190,
          height: 30,
          fontSize: 10,
          fontColor: '#000000',
        },
      ],
    ],
  };

  const lines = convertedRows.map(
    (r) => `${r.date} | ${r.establishment} | ${r.category} | EUR ${r.amountEur.toFixed(2)}`,
  );

  const summary = [
    `Subtotal: EUR ${subtotal.toFixed(2)}`,
    `Management Fee (5%): EUR ${fee.toFixed(2)}`,
    `Total: EUR ${total.toFixed(2)}`,
  ].join('\n');

  const inputs = [
    {
      title: 'Parent Company Expense Report (EUR)',
      content: lines.join('\n'),
      summary,
    },
  ];

  return generatePdf(template as unknown as Parameters<typeof generatePdf>[0], inputs);
}
