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

import {
  pgGetGeneralBanks,
  pgAddGeneralBank,
  pgUpdateGeneralBank,
  pgDeleteGeneralBank,
} from '@/lib/postgres/data-access';
import { pgQuery } from '@/lib/postgres/client';

type GeneralBankLike = {
  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 ensureGeneralBankAccountInChartPostgres(bank: GeneralBankLike): 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 generalBankPayloadSchema = z.object({
  code: z.string().trim().min(1, 'Bank code is required'),
  name: z.string().trim().min(1, 'Arabic name is required'),
  nameEn: z.string().trim().min(1, 'English name is required'),
  accountId: z.string().trim().optional(),
  accountCode: z.string().trim().optional(),
  swiftCode: z.string().trim().optional(),
  branchCode: z.string().trim().optional(),
  branch: z.string().trim().optional(),
  country: z.string().trim().optional(),
  city: z.string().trim().optional(),
  address: z.string().trim().optional(),
  phone: z.string().trim().optional(),
  email: z.string().email().optional().or(z.literal('')),
  ibanPattern: z.string().trim().optional(),
  notes: z.string().trim().optional(),
  isActive: z.boolean().optional(),
});

export async function GET() {
  try {
    const banks = await pgGetGeneralBanks();
    return NextResponse.json(banks);
  } catch (error) {
    console.error('Error fetching general banks:', error);
    return NextResponse.json(
      { error: 'Failed to fetch general banks' },
      { status: 500 }
    );
  }
}

export async function POST(request: NextRequest) {
  try {
    const body = await request.json();
    const parsed = generalBankPayloadSchema.safeParse(body);
    if (!parsed.success) {
      return NextResponse.json({ error: 'Invalid bank payload' }, { status: 400 });
    }
    const payload = parsed.data;

    let newBank = await pgAddGeneralBank(payload as any);

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

    return NextResponse.json(newBank, { status: 201 });
  } catch (error) {
    const message = error instanceof Error ? error.message : '';
    console.error('Error creating general bank:', error);
    if (message === 'GENERAL_BANK_CODE_EXISTS') {
      return NextResponse.json({ error: 'Bank code already exists' }, { status: 409 });
    }
    if (message === 'GENERAL_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 = generalBankPayloadSchema.safeParse(body);
    if (!parsed.success) {
      return NextResponse.json({ error: 'Invalid bank payload' }, { status: 400 });
    }

    const payload = parsed.data;
    let updated = await pgUpdateGeneralBank(id, payload);

    if (updated) {
      const next = updated as Record<string, any>;
      const link = await ensureGeneralBankAccountInChartPostgres({
        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 pgUpdateGeneralBank(id, {
        accountId: link.accountId,
        accountCode: link.accountCode,
      } as any);
    }
    
    if (!updated) {
      return NextResponse.json({ error: 'Bank not found' }, { status: 404 });
    }

    return NextResponse.json(updated);
  } catch (error) {
    const message = error instanceof Error ? error.message : '';
    console.error('Error updating general bank:', error);
    if (message === 'GENERAL_BANK_CODE_EXISTS') {
      return NextResponse.json({ error: 'Bank code already exists' }, { status: 409 });
    }
    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 id = request.nextUrl.searchParams.get('id');
    if (!id) {
      return NextResponse.json({ error: 'Bank id is required' }, { status: 400 });
    }

    const deleted = await pgDeleteGeneralBank(id);
    
    if (!deleted) {
      return NextResponse.json({ error: 'Bank not found' }, { status: 404 });
    }

    return NextResponse.json({ success: true });
  } catch (error) {
    console.error('Error deleting general bank:', error);
    return NextResponse.json(
      { error: 'Failed to delete bank' },
      { status: 500 }
    );
  }
}
