import { NextResponse } from 'next/server';
import { UserStatus } from '@prisma/client';
import { prisma } from '../../../../lib/prisma';
import { requireAuth } from '../../../../lib/jwt';
import { normalizeIban, normalizeNullableBoolean, normalizeOptionalString } from '../../../../lib/billing';

function pickTranslation(translations: { title: string; slug: string; locale: string }[]) {
  return translations.find((t) => t.locale === 'en') || translations.find((t) => t.locale === 'fi') || translations[0] || null;
}

async function loadState(userId: string) {
  const [user, listings] = await Promise.all([
    prisma.user.findUnique({
      where: { id: userId },
      select: {
        id: true,
        status: true,
        billingEmailsEnabled: true,
        billingAccountName: true,
        billingIban: true,
        billingIncludeVatLine: true,
      },
    }),
    prisma.listing.findMany({
      where: { ownerId: userId, removedAt: null },
      select: {
        id: true,
        billingAccountName: true,
        billingIban: true,
        billingIncludeVatLine: true,
        translations: { select: { title: true, slug: true, locale: true } },
      },
      orderBy: { createdAt: 'desc' },
    }),
  ]);
  return { user, listings };
}

function validateAccountName(name: string | null | undefined) {
  if (name && name.length > 120) {
    return 'Account owner name is too long';
  }
  return null;
}

function validateIban(iban: string | null | undefined) {
  if (iban && !/^[A-Z0-9]{8,34}$/.test(iban)) {
    return 'IBAN must be 8-34 alphanumeric characters';
  }
  return null;
}

function normalizeListingOverrides(body: any) {
  type OverrideInput = { id: string | null; accountName: string | null | undefined; iban: string | null | undefined; includeVatLine: boolean | null | undefined };
  const overridesRaw: any[] = Array.isArray(body?.listings) ? body.listings : [];
  const mapped: OverrideInput[] = overridesRaw.map((item: any) => ({
    id: typeof item.id === 'string' ? item.id : null,
    accountName: normalizeOptionalString(item.accountName),
    iban: normalizeIban(item.iban),
    includeVatLine: normalizeNullableBoolean(item.includeVatLine),
  }));
  return mapped.filter((o): o is OverrideInput & { id: string } => Boolean(o.id));
}

function buildResponsePayload(user: NonNullable<Awaited<ReturnType<typeof loadState>>['user']>, listings: Awaited<ReturnType<typeof loadState>>['listings']) {
  return {
    settings: {
      enabled: user.billingEmailsEnabled,
      accountName: user.billingAccountName,
      iban: user.billingIban,
      includeVatLine: user.billingIncludeVatLine,
    },
    listings: listings.map((listing) => {
      const translation = pickTranslation(listing.translations);
      return {
        id: listing.id,
        title: translation?.title ?? 'Listing',
        slug: translation?.slug ?? '',
        locale: translation?.locale ?? 'en',
        billingAccountName: listing.billingAccountName,
        billingIban: listing.billingIban,
        billingIncludeVatLine: listing.billingIncludeVatLine,
      };
    }),
  };
}

export async function GET(req: Request) {
  try {
    const session = await requireAuth(req);
    const { user, listings } = await loadState(session.userId);
    if (!user || user.status !== UserStatus.ACTIVE) {
      return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
    }
    return NextResponse.json(buildResponsePayload(user, listings));
  } catch (error) {
    console.error('Billing settings fetch failed', error);
    return NextResponse.json({ error: 'Failed to load billing settings' }, { status: 500 });
  }
}

export async function PATCH(req: Request) {
  let body: any;
  try {
    body = await req.json();
  } catch {
    return NextResponse.json({ error: 'Invalid request body' }, { status: 400 });
  }

  try {
    const session = await requireAuth(req);
    const { user, listings } = await loadState(session.userId);
    if (!user || user.status !== UserStatus.ACTIVE) {
      return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
    }

    const enabled = body.enabled === undefined ? undefined : Boolean(body.enabled);
    const accountName = normalizeOptionalString(body.accountName);
    const iban = normalizeIban(body.iban);
    const includeVatLine = body.includeVatLine === undefined ? undefined : Boolean(body.includeVatLine);
    const listingOverrides = normalizeListingOverrides(body);

    const errors = [validateAccountName(accountName), validateIban(iban)].filter(Boolean) as string[];
    for (const override of listingOverrides) {
      const nameError = validateAccountName(override.accountName);
      const ibanError = validateIban(override.iban);
      if (nameError) errors.push(`${nameError} for listing ${override.id}`);
      if (ibanError) errors.push(`${ibanError} for listing ${override.id}`);
    }
    if (errors.length) {
      return NextResponse.json({ error: errors.join('; ') }, { status: 400 });
    }

    const userUpdates: any = {};
    if (enabled !== undefined) userUpdates.billingEmailsEnabled = enabled;
    if (accountName !== undefined) userUpdates.billingAccountName = accountName;
    if (iban !== undefined) userUpdates.billingIban = iban;
    if (includeVatLine !== undefined) userUpdates.billingIncludeVatLine = includeVatLine;

    const listingMap = new Map(listings.map((l) => [l.id, l]));
    const listingUpdates: { id: string; data: Record<string, any> }[] = [];
    listingOverrides.forEach((override: { id: string; accountName: string | null | undefined; iban: string | null | undefined; includeVatLine: boolean | null | undefined }) => {
      if (!listingMap.has(override.id!)) return;
      const data: Record<string, any> = {};
      if (override.accountName !== undefined) data.billingAccountName = override.accountName;
      if (override.iban !== undefined) data.billingIban = override.iban;
      if (override.includeVatLine !== undefined) data.billingIncludeVatLine = override.includeVatLine;
      if (Object.keys(data).length) {
        listingUpdates.push({ id: override.id!, data });
      }
    });

    const targetEnabled = enabled ?? user.billingEmailsEnabled;
    if (targetEnabled) {
      const targetAccountName = accountName !== undefined ? accountName : user.billingAccountName;
      const targetIban = iban !== undefined ? iban : user.billingIban;
      const hasGlobalDetails = Boolean(targetAccountName && targetIban);

      const missingFor: string[] = [];
      listings.forEach((listing) => {
        const override = listingUpdates.find((o) => o.id === listing.id);
        const effectiveAccountName =
          override?.data.billingAccountName !== undefined
            ? override.data.billingAccountName
            : listing.billingAccountName ?? targetAccountName;
        const effectiveIban =
          override?.data.billingIban !== undefined ? override.data.billingIban : listing.billingIban ?? targetIban;
        if (!effectiveAccountName || !effectiveIban) {
          const t = pickTranslation(listing.translations);
          missingFor.push(t?.slug || listing.id);
        }
      });

      if (!hasGlobalDetails && missingFor.length) {
        return NextResponse.json(
          { error: `Provide billing account name and IBAN globally or per listing (missing for: ${missingFor.join(', ')})` },
          { status: 400 },
        );
      }
      if (!hasGlobalDetails && listings.length === 0) {
        return NextResponse.json(
          { error: 'Add a billing account name and IBAN before enabling the billing assistant.' },
          { status: 400 },
        );
      }
    }

    const tx: any[] = [];
    if (Object.keys(userUpdates).length) {
      tx.push(prisma.user.update({ where: { id: user.id }, data: userUpdates }));
    }
    listingUpdates.forEach((update) => {
      tx.push(prisma.listing.update({ where: { id: update.id }, data: update.data }));
    });

    if (tx.length) {
      await prisma.$transaction(tx);
    } else {
      return NextResponse.json({ error: 'No updates provided' }, { status: 400 });
    }

    const { user: updatedUser, listings: updatedListings } = await loadState(session.userId);
    if (!updatedUser) {
      return NextResponse.json({ error: 'User not found' }, { status: 404 });
    }

    return NextResponse.json(buildResponsePayload(updatedUser, updatedListings));
  } catch (error) {
    console.error('Billing settings update failed', error);
    const status = (error as any)?.message === 'Unauthorized' ? 401 : 500;
    return NextResponse.json({ error: 'Failed to update billing settings' }, { status });
  }
}

export const dynamic = 'force-dynamic';
