import db from './sqlite';

const BASE_CURRENCY = 'CNY';

interface TransactionRow {
  id: number;
  type: string;
  amount: number;
  currency: string;
  exchange_rate_to_base: number;
  amount_in_base: number;
  category_id: null | number;
  account_id: null | number;
  transaction_date: string;
  description: null | string;
  project: null | string;
  memo: null | string;
  created_at: string;
  status: string;
  status_updated_at: null | string;
  reimbursement_batch: null | string;
  review_notes: null | string;
  submitted_by: null | string;
  approved_by: null | string;
  approved_at: null | string;
  is_deleted: number;
  deleted_at: null | string;
}

interface TransactionPayload {
  type: string;
  amount: number;
  currency: string;
  categoryId?: null | number;
  accountId?: null | number;
  transactionDate: string;
  description?: string;
  project?: null | string;
  memo?: null | string;
  createdAt?: string;
  isDeleted?: boolean;
  status?: TransactionStatus;
  statusUpdatedAt?: string;
  reimbursementBatch?: null | string;
  reviewNotes?: null | string;
  submittedBy?: null | string;
  approvedBy?: null | string;
  approvedAt?: null | string;
}

export type TransactionStatus =
  | 'draft'
  | 'pending'
  | 'approved'
  | 'rejected'
  | 'paid';

function getExchangeRateToBase(currency: string) {
  if (currency === BASE_CURRENCY) {
    return 1;
  }
  const stmt = db.prepare(
    `SELECT rate FROM finance_exchange_rates WHERE from_currency = ? AND to_currency = ? ORDER BY date DESC LIMIT 1`,
  );
  const row = stmt.get(currency, BASE_CURRENCY) as undefined | { rate: number };
  return row?.rate ?? 1;
}

function mapTransaction(row: TransactionRow) {
  return {
    id: row.id,
    userId: 1,
    type: 'expense' as const,
    amount: Math.abs(row.amount),
    currency: row.currency,
    exchangeRateToBase: row.exchange_rate_to_base,
    amountInBase: Math.abs(row.amount_in_base),
    categoryId: row.category_id ?? undefined,
    accountId: row.account_id ?? undefined,
    transactionDate: row.transaction_date,
    description: row.description ?? '',
    project: row.project ?? undefined,
    memo: row.memo ?? undefined,
    createdAt: row.created_at,
    status: row.status as TransactionStatus,
    statusUpdatedAt: row.status_updated_at ?? undefined,
    reimbursementBatch: row.reimbursement_batch ?? undefined,
    reviewNotes: row.review_notes ?? undefined,
    submittedBy: row.submitted_by ?? undefined,
    approvedBy: row.approved_by ?? undefined,
    approvedAt: row.approved_at ?? undefined,
    isDeleted: Boolean(row.is_deleted),
    deletedAt: row.deleted_at ?? undefined,
  };
}

export function fetchTransactions(
  options: {
    includeDeleted?: boolean;
    type?: string;
    statuses?: TransactionStatus[];
  } = {},
) {
  const clauses: string[] = [];
  const params: Record<string, unknown> = {};

  if (!options.includeDeleted) {
    clauses.push('is_deleted = 0');
  }
  if (options.type) {
    clauses.push('type = @type');
    params.type = options.type;
  }
  if (options.statuses && options.statuses.length > 0) {
    clauses.push(
      `status IN (${options.statuses.map((_, index) => `@status${index}`).join(', ')})`,
    );
    options.statuses.forEach((status, index) => {
      params[`status${index}`] = status;
    });
  }

  const where = clauses.length > 0 ? `WHERE ${clauses.join(' AND ')}` : '';

  const stmt = db.prepare<TransactionRow>(
    `SELECT id, type, amount, currency, exchange_rate_to_base, amount_in_base, category_id, account_id, transaction_date, description, project, memo, created_at, status, status_updated_at, reimbursement_batch, review_notes, submitted_by, approved_by, approved_at, is_deleted, deleted_at FROM finance_transactions ${where} ORDER BY transaction_date DESC, id DESC`,
  );

  return stmt.all(params).map(mapTransaction);
}

export function getTransactionById(id: number) {
  const stmt = db.prepare<TransactionRow>(
    `SELECT id, type, amount, currency, exchange_rate_to_base, amount_in_base, category_id, account_id, transaction_date, description, project, memo, created_at, status, status_updated_at, reimbursement_batch, review_notes, submitted_by, approved_by, approved_at, is_deleted, deleted_at FROM finance_transactions WHERE id = ?`,
  );
  const row = stmt.get(id);
  return row ? mapTransaction(row) : null;
}

export function createTransaction(payload: TransactionPayload) {
  const exchangeRate = getExchangeRateToBase(payload.currency);
  const amountInBase = +(payload.amount * exchangeRate).toFixed(2);
  const createdAt =
    payload.createdAt && payload.createdAt.length > 0
      ? payload.createdAt
      : new Date().toISOString();
  const status: TransactionStatus = payload.status ?? 'approved';
  const statusUpdatedAt =
    payload.statusUpdatedAt && payload.statusUpdatedAt.length > 0
      ? payload.statusUpdatedAt
      : createdAt;
  const approvedAt =
    payload.approvedAt && payload.approvedAt.length > 0
      ? payload.approvedAt
      : status === 'approved' || status === 'paid'
        ? statusUpdatedAt
        : null;

  const stmt = db.prepare(
    `INSERT INTO finance_transactions (type, amount, currency, exchange_rate_to_base, amount_in_base, category_id, account_id, transaction_date, description, project, memo, created_at, status, status_updated_at, reimbursement_batch, review_notes, submitted_by, approved_by, approved_at, is_deleted) VALUES (@type, @amount, @currency, @exchangeRateToBase, @amountInBase, @categoryId, @accountId, @transactionDate, @description, @project, @memo, @createdAt, @status, @statusUpdatedAt, @reimbursementBatch, @reviewNotes, @submittedBy, @approvedBy, @approvedAt, 0)`,
  );

  const info = stmt.run({
    type: payload.type,
    amount: payload.amount,
    currency: payload.currency,
    exchangeRateToBase: exchangeRate,
    amountInBase,
    categoryId: payload.categoryId ?? null,
    accountId: payload.accountId ?? null,
    transactionDate: payload.transactionDate,
    description: payload.description ?? '',
    project: payload.project ?? null,
    memo: payload.memo ?? null,
    createdAt,
    status,
    statusUpdatedAt,
    reimbursementBatch: payload.reimbursementBatch ?? null,
    reviewNotes: payload.reviewNotes ?? null,
    submittedBy: payload.submittedBy ?? null,
    approvedBy: payload.approvedBy ?? null,
    approvedAt,
  });

  return getTransactionById(Number(info.lastInsertRowid));
}

export function updateTransaction(id: number, payload: TransactionPayload) {
  const current = getTransactionById(id);
  if (!current) {
    return null;
  }

  const nextStatus = (payload.status ?? current.status ?? 'approved') as TransactionStatus;
  const statusChanged = nextStatus !== current.status;
  const statusUpdatedAt =
    payload.statusUpdatedAt && payload.statusUpdatedAt.length > 0
      ? payload.statusUpdatedAt
      : statusChanged
        ? new Date().toISOString()
        : current.statusUpdatedAt ?? current.createdAt;
  const approvedAt =
    payload.approvedAt && payload.approvedAt.length > 0
      ? payload.approvedAt
      : nextStatus === 'approved' || nextStatus === 'paid'
        ? current.approvedAt ?? (statusChanged ? statusUpdatedAt : null)
        : null;
  const approvedBy =
    nextStatus === 'approved' || nextStatus === 'paid'
      ? payload.approvedBy ?? current.approvedBy ?? null
      : payload.approvedBy ?? null;

  const next = {
    type: payload.type ?? current.type,
    amount: payload.amount ?? current.amount,
    currency: payload.currency ?? current.currency,
    categoryId: payload.categoryId ?? current.categoryId ?? null,
    accountId: payload.accountId ?? current.accountId ?? null,
    transactionDate: payload.transactionDate ?? current.transactionDate,
    description: payload.description ?? current.description ?? '',
    project: payload.project ?? current.project ?? null,
    memo: payload.memo ?? current.memo ?? null,
    isDeleted: payload.isDeleted ?? current.isDeleted,
    status: nextStatus,
    statusUpdatedAt,
    reimbursementBatch:
      payload.reimbursementBatch ?? current.reimbursementBatch ?? null,
    reviewNotes: payload.reviewNotes ?? current.reviewNotes ?? null,
    submittedBy: payload.submittedBy ?? current.submittedBy ?? null,
    approvedBy,
    approvedAt,
  };

  const exchangeRate = getExchangeRateToBase(next.currency);
  const amountInBase = +(next.amount * exchangeRate).toFixed(2);

  const stmt = db.prepare(
    `UPDATE finance_transactions SET type = @type, amount = @amount, currency = @currency, exchange_rate_to_base = @exchangeRateToBase, amount_in_base = @amountInBase, category_id = @categoryId, account_id = @accountId, transaction_date = @transactionDate, description = @description, project = @project, memo = @memo, status = @status, status_updated_at = @statusUpdatedAt, reimbursement_batch = @reimbursementBatch, review_notes = @reviewNotes, submitted_by = @submittedBy, approved_by = @approvedBy, approved_at = @approvedAt, is_deleted = @isDeleted, deleted_at = @deletedAt WHERE id = @id`,
  );

  const deletedAt = next.isDeleted ? new Date().toISOString() : null;

  stmt.run({
    id,
    type: next.type,
    amount: next.amount,
    currency: next.currency,
    exchangeRateToBase: exchangeRate,
    amountInBase,
    categoryId: next.categoryId,
    accountId: next.accountId,
    transactionDate: next.transactionDate,
    description: next.description,
    project: next.project,
    memo: next.memo,
    status: next.status,
    statusUpdatedAt: next.statusUpdatedAt,
    reimbursementBatch: next.reimbursementBatch,
    reviewNotes: next.reviewNotes,
    submittedBy: next.submittedBy,
    approvedBy: next.approvedBy,
    approvedAt: next.approvedAt,
    isDeleted: next.isDeleted ? 1 : 0,
    deletedAt,
  });

  return getTransactionById(id);
}

export function softDeleteTransaction(id: number) {
  const stmt = db.prepare(
    `UPDATE finance_transactions SET is_deleted = 1, deleted_at = @deletedAt WHERE id = @id`,
  );
  stmt.run({ id, deletedAt: new Date().toISOString() });
  return getTransactionById(id);
}

export function restoreTransaction(id: number) {
  const stmt = db.prepare(
    `UPDATE finance_transactions SET is_deleted = 0, deleted_at = NULL WHERE id = @id`,
  );
  stmt.run({ id });
  return getTransactionById(id);
}

export function replaceAllTransactions(
  rows: Array<{
    accountId: null | number;
    amount: number;
    categoryId: null | number;
    createdAt?: string;
    currency: string;
    description: string;
    memo?: null | string;
    project?: null | string;
    transactionDate: string;
    type: string;
    status?: TransactionStatus;
    statusUpdatedAt?: string;
    reimbursementBatch?: null | string;
    reviewNotes?: null | string;
    submittedBy?: null | string;
    approvedBy?: null | string;
    approvedAt?: null | string;
    isDeleted?: boolean;
  }>,
) {
  db.prepare('DELETE FROM finance_transactions').run();

  const insert = db.prepare(
    `INSERT INTO finance_transactions (type, amount, currency, exchange_rate_to_base, amount_in_base, category_id, account_id, transaction_date, description, project, memo, created_at, status, status_updated_at, reimbursement_batch, review_notes, submitted_by, approved_by, approved_at, is_deleted) VALUES (@type, @amount, @currency, @exchangeRateToBase, @amountInBase, @categoryId, @accountId, @transactionDate, @description, @project, @memo, @createdAt, @status, @statusUpdatedAt, @reimbursementBatch, @reviewNotes, @submittedBy, @approvedBy, @approvedAt, @isDeleted)`,
  );

  const getRate = db.prepare(
    `SELECT rate FROM finance_exchange_rates WHERE from_currency = ? AND to_currency = 'CNY' ORDER BY date DESC LIMIT 1`,
  );

  const insertMany = db.transaction((items: Array<any>) => {
    for (const item of items) {
      const row = getRate.get(item.currency) as undefined | { rate: number };
      const rate = row?.rate ?? 1;
      const amountInBase = +(item.amount * rate).toFixed(2);
      const createdAt =
        item.createdAt ??
        new Date(`${item.transactionDate}T00:00:00Z`).toISOString();
      const status = item.status ?? 'approved';
      const statusUpdatedAt =
        item.statusUpdatedAt ??
        new Date(
          `${item.transactionDate}T00:00:00Z`,
        ).toISOString();
      const approvedAt =
        item.approvedAt ??
        (status === 'approved' || status === 'paid' ? statusUpdatedAt : null);
      insert.run({
        ...item,
        exchangeRateToBase: rate,
        amountInBase,
        project: item.project ?? null,
        memo: item.memo ?? null,
        createdAt,
        status,
        statusUpdatedAt,
        reimbursementBatch: item.reimbursementBatch ?? null,
        reviewNotes: item.reviewNotes ?? null,
        submittedBy: item.submittedBy ?? null,
        approvedBy:
          status === 'approved' || status === 'paid'
            ? item.approvedBy ?? null
            : null,
        approvedAt,
        isDeleted: item.isDeleted ? 1 : 0,
      });
    }
  });

  insertMany(rows);
}

// 分类相关函数
interface CategoryRow {
  id: number;
  name: string;
  type: string;
  icon: null | string;
  color: null | string;
  user_id: null | number;
  is_active: number;
}

function mapCategory(row: CategoryRow) {
  return {
    id: row.id,
    userId: row.user_id ?? null,
    name: row.name,
    type: row.type as 'expense' | 'income',
    icon: row.icon ?? '📝',
    color: row.color ?? '#dfe4ea',
    sortOrder: row.id,
    isSystem: row.user_id === null,
    isActive: Boolean(row.is_active),
  };
}

export function fetchCategories(options: { type?: 'expense' | 'income' } = {}) {
  const where = options.type
    ? `WHERE type = @type AND is_active = 1`
    : 'WHERE is_active = 1';
  const params = options.type ? { type: options.type } : {};

  const stmt = db.prepare<CategoryRow>(
    `SELECT id, name, type, icon, color, user_id, is_active FROM finance_categories ${where} ORDER BY id ASC`,
  );

  return stmt.all(params).map(mapCategory);
}
