import { NextRequest, NextResponse } from 'next/server';
import * as z from 'zod';

import {
  pgGetBanks,
  pgCreateBank,
  pgUpdateBank,
  pgDeleteBank,
} from '@/lib/postgres/data-access';
import { pgQuery } from '@/lib/postgres/client';

function normalizeBankCodeInput(value: string): string {
  const normalized = String(value || '')
    .trim()
    .toUpperCase()
    .replace(/\s+/g, '-')
    .replace(/[^A-Z0-9_-]/g, '')
    .replace(/-+/g, '-')
    .replace(/^[-_]+|[-_]+$/g, '')
    .slice(0, 30);
  return normalized;
}

function buildBankCodeSeed(payload: z.infer<typeof bankPayloadSchema>): string {
  const provided = normalizeBankCodeInput(payload.bankCode || payload.code || '');
  if (provided) return provided;

  const fromName = normalizeBankCodeInput(payload.name || payload.fullName || payload.nameEn || '');
  if (fromName) return fromName;

  return 'BANK';
}

function codeCandidate(base: string, attempt: number): string {
  if (attempt <= 0) return base;
  const suffix = String(attempt + 1);
  const maxBaseLen = Math.max(1, 30 - (suffix.length + 1));
  return `${base.slice(0, maxBaseLen)}-${suffix}`;
}

type BankLike = {
  id: string;
  name?: string;
  nameEn?: string;
  accountId?: string;
  accountCode?: string;
};

function normalizeAccountCode(value: string): string {
  return String(value || '').trim().toUpperCase();
}

function buildBankAccountRaw(params: { code: string; nameAr: string; nameEn: string }) {
  return {
    id: params.code,
    code: params.code,
    nameAr: params.nameAr,
    nameEn: params.nameEn,
    type: 'account',
    parentId: '1120',
    nature: 'debit',
    inactive: false,
  };
}

async function ensureBankAccountInChartPostgres(bank: BankLike): Promise<{ accountId: string; accountCode: string }> {
  const bankNameAr = String(bank.name || '').trim() || 'بنك';
  const bankNameEn = String(bank.nameEn || bank.name || '').trim() || 'Bank';

  const ensureHeader = async (code: string, parentId: string | null, nameAr: string, nameEn: string) => {
    const raw = {
      id: code,
      code,
      nameAr,
      nameEn,
      type: parentId ? 'account' : 'header',
      parentId,
      nature: parentId ? 'debit' : undefined,
      inactive: false,
    };
    await pgQuery(
      `
      INSERT INTO chart_of_accounts (id, raw, updated_at)
      VALUES ($1, $2::jsonb, NOW())
      ON CONFLICT (id)
      DO UPDATE SET raw = EXCLUDED.raw, updated_at = NOW()
      `,
      [code, JSON.stringify(raw)]
    );
  };

  await ensureHeader('1100', null, 'الأصول المتداولة', 'Current Assets');
  await ensureHeader('1120', '1100', 'البنوك', 'Banks');

  const requestedCode = normalizeAccountCode(bank.accountCode || '');
  if (requestedCode && requestedCode !== '1120') {
    const accountRaw = buildBankAccountRaw({
      code: requestedCode,
      nameAr: `بنك - ${bankNameAr}`,
      nameEn: `Bank - ${bankNameEn}`,
    });

    await pgQuery(
      `
      INSERT INTO chart_of_accounts (id, raw, updated_at)
      VALUES ($1, $2::jsonb, NOW())
      ON CONFLICT (id)
      DO UPDATE SET raw = EXCLUDED.raw, updated_at = NOW()
      `,
      [requestedCode, JSON.stringify(accountRaw)]
    );

    return { accountId: requestedCode, accountCode: requestedCode };
  }

  for (let attempt = 0; attempt < 60; attempt += 1) {
    const maxRow = await pgQuery<{ max_seq: string }>(
      `
      SELECT COALESCE(MAX((regexp_match(raw->>'code', '^1120-([0-9]+)$'))[1]::int), 0)::text AS max_seq
      FROM chart_of_accounts
      WHERE raw->>'code' ~ '^1120-[0-9]+$'
      `
    );
    const baseSeq = Number(maxRow.rows[0]?.max_seq || 0);
    const seq = baseSeq + 1 + attempt;
    const code = `1120-${String(seq).padStart(3, '0')}`;
    const accountRaw = buildBankAccountRaw({
      code,
      nameAr: `بنك - ${bankNameAr}`,
      nameEn: `Bank - ${bankNameEn}`,
    });

    const inserted = await pgQuery<{ id: string }>(
      `
      INSERT INTO chart_of_accounts (id, raw, updated_at)
      VALUES ($1, $2::jsonb, NOW())
      ON CONFLICT (id) DO NOTHING
      RETURNING id
      `,
      [code, JSON.stringify(accountRaw)]
    );

    if ((inserted.rowCount || 0) > 0) {
      return { accountId: code, accountCode: code };
    }
  }

  throw new Error('BANK_ACCOUNT_AUTO_LINK_FAILED');
}

const bankPayloadSchema = z.object({
  bankCode: z.string().trim().optional().default(''),
  code: z.string().trim().optional().default(''),
  name: z.string().trim().min(1, 'Bank name is required'),
  nameEn: z.string().trim().optional().default(''),
  accountId: z.string().trim().optional().default(''),
  accountCode: z.string().trim().optional().default(''),
  accountNumber: z.string().trim().optional().default(''),
  bankNumber: z.string().trim().optional().default(''),
  fullName: z.string().trim().optional().default(''),
  branch: z.string().trim().optional().default(''),
  iban: z.string().trim().optional().default(''),
  swiftCode: z.string().trim().optional().default(''),
  contactName: z.string().trim().optional().default(''),
  contactPhone: z.string().trim().optional().default(''),
  notes: z.string().trim().optional().default(''),
  isActive: z.coerce.boolean().optional().default(true),
  currencyCode: z.string().trim().optional().default(''),
});

export async function GET() {
  try {
    const banks = (await pgGetBanks({ page: 1, pageSize: 5000 })).items;
    return NextResponse.json(banks);
  } catch (error) {
    return NextResponse.json(
      { error: 'Failed to fetch banks' },
      { status: 500 }
    );
  }
}

export async function POST(request: NextRequest) {
  try {
    const body = await request.json();
    const parsed = bankPayloadSchema.safeParse(body);
    if (!parsed.success) {
      return NextResponse.json({ error: 'Invalid bank payload' }, { status: 400 });
    }
    const payload = parsed.data;
    const baseCode = buildBankCodeSeed(payload);
    const hasManualCode = !!normalizeBankCodeInput(payload.bankCode || payload.code || '');

    const maxAttempts = hasManualCode ? 1 : 15;
    let newBank: any = null;

    for (let attempt = 0; attempt < maxAttempts; attempt += 1) {
      const normalized = {
        code: codeCandidate(baseCode, attempt),
        name: payload.name,
        nameEn: payload.nameEn || payload.fullName || payload.name,
        accountId: payload.accountId || undefined,
        accountCode: payload.accountCode || undefined,
        accountNumber: payload.accountNumber || undefined,
        bankNumber: payload.bankNumber || undefined,
        fullName: payload.fullName || payload.name,
        branch: payload.branch || undefined,
        iban: payload.iban || undefined,
        swiftCode: payload.swiftCode || undefined,
        contactName: payload.contactName || undefined,
        contactPhone: payload.contactPhone || undefined,
        notes: payload.notes || undefined,
        isActive: payload.isActive !== false,
        currencyCode: payload.currencyCode || undefined,
      };

      try {
        newBank = await pgCreateBank({
              id: `bank-${Date.now()}-${attempt}`,
              createdAt: new Date().toISOString(),
              createdBy: 'system',
              ...normalized,
            });
        break;
      } catch (error: any) {
        const message = String(error?.message || '');
        const isCodeConflict = message === 'BANK_CODE_EXISTS';
        if (!isCodeConflict || hasManualCode || attempt === maxAttempts - 1) {
          throw error;
        }
      }
    }

    if (!newBank) {
      return NextResponse.json({ error: 'Failed to generate unique bank code' }, { status: 409 });
    }

    
    const created = newBank as Record<string, any>;
    const link = await ensureBankAccountInChartPostgres({
    id: String(created.id || ''),
    name: String(created.name || ''),
    nameEn: String(created.nameEn || created.name || ''),
    accountCode: String(created.accountCode || ''),
    accountId: String(created.accountId || ''),
    });
    newBank = await pgUpdateBank(String(created.id), {
    accountId: link.accountId,
    accountCode: link.accountCode,
    });
    

    return NextResponse.json(newBank, { status: 201 });
  } catch (error) {
    const message = error instanceof Error ? error.message : '';
    if (message === 'BANK_CODE_EXISTS') {
      return NextResponse.json({ error: 'Bank code already exists' }, { status: 409 });
    }
    if (message === 'BANK_CODE_REQUIRED') {
      return NextResponse.json({ error: 'Bank code is required' }, { status: 400 });
    }
    if (message === 'BANK_ACCOUNT_AUTO_LINK_FAILED') {
      return NextResponse.json({ error: 'Failed to auto-link bank account in chart of accounts' }, { status: 500 });
    }
    return NextResponse.json({ error: 'Failed to add bank' }, { status: 500 });
  }
}

export async function PUT(request: NextRequest) {
  try {
    const body = await request.json();
    const id = String(body?.id || '').trim();
    if (!id) {
      return NextResponse.json({ error: 'Bank id is required' }, { status: 400 });
    }

    const parsed = bankPayloadSchema.safeParse(body);
    if (!parsed.success) {
      return NextResponse.json({ error: 'Invalid bank payload' }, { status: 400 });
    }

    const payload = parsed.data;
    const normalized = {
      code: payload.bankCode || payload.code || payload.name,
      name: payload.name,
      nameEn: payload.nameEn || payload.fullName || payload.name,
      accountId: payload.accountId || undefined,
      accountCode: payload.accountCode || undefined,
      accountNumber: payload.accountNumber || undefined,
      bankNumber: payload.bankNumber || undefined,
      fullName: payload.fullName || payload.name,
      branch: payload.branch || undefined,
      iban: payload.iban || undefined,
      swiftCode: payload.swiftCode || undefined,
      contactName: payload.contactName || undefined,
      contactPhone: payload.contactPhone || undefined,
      notes: payload.notes || undefined,
      isActive: payload.isActive !== false,
      currencyCode: payload.currencyCode || undefined,
    };

    let updated = await pgUpdateBank(id, normalized);

    if (updated) {
      const next = updated as Record<string, any>;
      const link = await ensureBankAccountInChartPostgres({
        id: String(next.id || id),
        name: String(next.name || ''),
        nameEn: String(next.nameEn || next.name || ''),
        accountCode: String(next.accountCode || ''),
        accountId: String(next.accountId || ''),
      });
      updated = await pgUpdateBank(id, {
        accountId: link.accountId,
        accountCode: link.accountCode,
      });
    }

    if (!updated) {
      return NextResponse.json({ error: 'Bank not found' }, { status: 404 });
    }

    return NextResponse.json(updated);
  } catch (error) {
    const message = error instanceof Error ? error.message : '';
    if (message === 'BANK_CODE_EXISTS') {
      return NextResponse.json({ error: 'Bank code already exists' }, { status: 409 });
    }
    if (message === 'BANK_CODE_REQUIRED') {
      return NextResponse.json({ error: 'Bank code is required' }, { status: 400 });
    }
    if (message === 'BANK_ACCOUNT_AUTO_LINK_FAILED') {
      return NextResponse.json({ error: 'Failed to auto-link bank account in chart of accounts' }, { status: 500 });
    }
    return NextResponse.json({ error: 'Failed to update bank' }, { status: 500 });
  }
}

export async function DELETE(request: NextRequest) {
  try {
    const { searchParams } = new URL(request.url);
    const id = String(searchParams.get('id') || '').trim();
    if (!id) {
      return NextResponse.json({ error: 'Bank id is required' }, { status: 400 });
    }
    const deleted = await pgDeleteBank(id);
    if (!deleted) {
      return NextResponse.json({ error: 'Bank not found' }, { status: 404 });
    }
    return NextResponse.json({ success: true });
  } catch (error) {
    return NextResponse.json({ error: 'Failed to delete bank' }, { status: 500 });
  }
}
