import { NextResponse } from "next/server";

import { ensureDefaultCurrencies } from "@/features/currencies/lib/default-currencies";
import { serializeBank } from "@/features/banks/lib/serialize-bank";
import { serializeCurrency } from "@/features/currencies/lib/serialize-currency";
import { buildSettingsDiff } from "@/features/settings/lib/settings-diff";
import { appSettingsSchema } from "@/features/settings/schemas/settings.schema";
import {
  getAppSettings,
  getAppSettingsRecord,
  serializeAppSettings
} from "@/lib/app-settings";
import { createAuditTrailSafe } from "@/lib/audit-trail";
import { requireApiAccess } from "@/lib/authorization";
import { resetMailerTransport } from "@/lib/mailer";
import { prisma } from "@/lib/prisma";

export async function GET(request: Request) {
  const auth = requireApiAccess(request);
  if (!auth.ok) return auth.response;

  await ensureDefaultCurrencies();
  const settings = await getAppSettings();
  const [currencies, banks] = await Promise.all([
    prisma.currency.findMany({
      where: {
        OR: [{ status: "ACTIVE" }, { code: settings.currency_code }]
      },
      orderBy: [{ code: "asc" }]
    }),
    prisma.bank.findMany({
      where: {
        OR: [
          { status: "ACTIVE" },
          { name: settings.company_bank_name },
          {
            id: {
              in: settings.company_bank_accounts.map((account) => BigInt(account.bank_id))
            }
          }
        ]
      },
      orderBy: [{ code: "asc" }]
    })
  ]);

  return NextResponse.json({
    data: settings,
    options: {
      currencies: currencies.map(serializeCurrency),
      banks: banks.map(serializeBank)
    }
  });
}

export async function PUT(request: Request) {
  const auth = requireApiAccess(request);
  if (!auth.ok) return auth.response;

  try {
    await ensureDefaultCurrencies();
    const parsed = appSettingsSchema.safeParse(await request.json());
    if (!parsed.success) {
      return NextResponse.json(
        {
          message: "Validasi gagal",
          errors: parsed.error.flatten().fieldErrors
        },
        { status: 422 }
      );
    }

    const beforeRecord = await getAppSettingsRecord();
    const before = serializeAppSettings(beforeRecord);
    const normalizedCurrencyCode = parsed.data.currency_code.toUpperCase();
    const currency = await prisma.currency.findFirst({
      where: {
        code: normalizedCurrencyCode,
        status: "ACTIVE"
      }
    });

    if (!currency) {
      return NextResponse.json(
        {
          message: "Validasi gagal",
          errors: {
            currency_code: ["Mata uang harus dipilih dari master currency yang aktif"]
          }
        },
        { status: 422 }
      );
    }

    const companyBankAccounts = parsed.data.company_bank_accounts;
    const companyBankIds = companyBankAccounts.map((account) => BigInt(account.bank_id));
    if (companyBankIds.length > 0) {
      const banks = await prisma.bank.findMany({
        where: {
          id: { in: companyBankIds },
          status: "ACTIVE"
        }
      });

      if (banks.length !== companyBankIds.length) {
        return NextResponse.json(
          {
            message: "Validasi gagal",
            errors: {
              company_bank_accounts: ["Semua rekening kantor harus memilih bank aktif dari master"]
            }
          },
          { status: 422 }
        );
      }
    }

    const primaryCompanyBankAccount = companyBankAccounts[0] ?? null;
    const primaryBank =
      primaryCompanyBankAccount === null
        ? null
        : await prisma.bank.findUnique({
            where: { id: BigInt(primaryCompanyBankAccount.bank_id) }
          });

    const updated = await prisma.appSetting.upsert({
      where: {
        singletonKey: "SYSTEM"
      },
      update: {
        companyName: parsed.data.company_name || null,
        companyEmail: parsed.data.company_email || null,
        companyPhone: parsed.data.company_phone || null,
        companyBankName: primaryBank?.name ?? null,
        companyBankAccountNumber: primaryCompanyBankAccount?.account_number || null,
        companyAddress: parsed.data.company_address || null,
        companyTimezone: parsed.data.company_timezone,
        smtpHost: parsed.data.smtp_host || null,
        smtpPort: parsed.data.smtp_port,
        smtpSecure: parsed.data.smtp_secure,
        smtpUser: parsed.data.smtp_user || null,
        smtpPassword:
          parsed.data.smtp_password === ""
            ? beforeRecord?.smtpPassword ?? null
            : parsed.data.smtp_password,
        smtpFromName: parsed.data.smtp_from_name || null,
        smtpFromEmail: parsed.data.smtp_from_email || null,
        purchasePrefix: parsed.data.purchase_prefix.toUpperCase(),
        receiptPrefix: parsed.data.receipt_prefix.toUpperCase(),
        lotPrefix: parsed.data.lot_prefix.toUpperCase(),
        adjustmentPrefix: parsed.data.adjustment_prefix.toUpperCase(),
        transformationPrefix: parsed.data.transformation_prefix.toUpperCase(),
        fundRequestPrefix: parsed.data.fund_request_prefix.toUpperCase(),
        washingPrefix: parsed.data.washing_prefix.toUpperCase(),
        regularSalePrefix: parsed.data.regular_sale_prefix.toUpperCase(),
        jitSalePrefix: parsed.data.jit_sale_prefix.toUpperCase(),
        consignmentPrefix: parsed.data.consignment_prefix.toUpperCase(),
        defaultLocale: parsed.data.default_locale,
        currencyCode: normalizedCurrencyCode,
        dateFormat: parsed.data.date_format,
        passwordMinLength: parsed.data.password_min_length,
        sessionTimeoutMinutes: parsed.data.session_timeout_minutes,
        requireEmailVerification: parsed.data.require_email_verification,
        auditRetentionDays: parsed.data.audit_retention_days,
        companyBankAccounts: {
          deleteMany: {},
          create: companyBankAccounts.map((account) => ({
            bankId: BigInt(account.bank_id),
            accountNumber: account.account_number
          }))
        }
      },
      create: {
        singletonKey: "SYSTEM",
        companyName: parsed.data.company_name || null,
        companyEmail: parsed.data.company_email || null,
        companyPhone: parsed.data.company_phone || null,
        companyBankName: primaryBank?.name ?? null,
        companyBankAccountNumber: primaryCompanyBankAccount?.account_number || null,
        companyAddress: parsed.data.company_address || null,
        companyTimezone: parsed.data.company_timezone,
        smtpHost: parsed.data.smtp_host || null,
        smtpPort: parsed.data.smtp_port,
        smtpSecure: parsed.data.smtp_secure,
        smtpUser: parsed.data.smtp_user || null,
        smtpPassword: parsed.data.smtp_password || null,
        smtpFromName: parsed.data.smtp_from_name || null,
        smtpFromEmail: parsed.data.smtp_from_email || null,
        purchasePrefix: parsed.data.purchase_prefix.toUpperCase(),
        receiptPrefix: parsed.data.receipt_prefix.toUpperCase(),
        lotPrefix: parsed.data.lot_prefix.toUpperCase(),
        adjustmentPrefix: parsed.data.adjustment_prefix.toUpperCase(),
        transformationPrefix: parsed.data.transformation_prefix.toUpperCase(),
        fundRequestPrefix: parsed.data.fund_request_prefix.toUpperCase(),
        washingPrefix: parsed.data.washing_prefix.toUpperCase(),
        regularSalePrefix: parsed.data.regular_sale_prefix.toUpperCase(),
        jitSalePrefix: parsed.data.jit_sale_prefix.toUpperCase(),
        consignmentPrefix: parsed.data.consignment_prefix.toUpperCase(),
        defaultLocale: parsed.data.default_locale,
        currencyCode: normalizedCurrencyCode,
        dateFormat: parsed.data.date_format,
        passwordMinLength: parsed.data.password_min_length,
        sessionTimeoutMinutes: parsed.data.session_timeout_minutes,
        requireEmailVerification: parsed.data.require_email_verification,
        auditRetentionDays: parsed.data.audit_retention_days,
        companyBankAccounts: {
          create: companyBankAccounts.map((account) => ({
            bankId: BigInt(account.bank_id),
            accountNumber: account.account_number
          }))
        }
      }
    });

    resetMailerTransport();

    const updatedRecord = await getAppSettingsRecord();
    const after = serializeAppSettings(updatedRecord);

    await createAuditTrailSafe({
      userId: auth.user.id,
      action: "SETTINGS_UPDATED",
      entityType: "SETTING",
      entityId: updated.id,
      method: request.method,
      pathname: new URL(request.url).pathname,
      statusCode: 200,
      summary: "Pengaturan sistem diperbarui",
      metadata: {
        before,
        after,
        changes: buildSettingsDiff(before, after)
      }
    });

    return NextResponse.json({
      data: after,
      message: "Pengaturan berhasil disimpan."
    });
  } catch (error) {
    return NextResponse.json(
      {
        message: error instanceof Error ? error.message : "Gagal menyimpan pengaturan"
      },
      { status: 500 }
    );
  }
}
