import { NextResponse } from 'next/server';
import { ListingStatus, UserStatus, EvCharging, Prisma } from '@prisma/client';
import { prisma } from '../../../lib/prisma';
import { requireAuth } from '../../../lib/jwt';
import { resolveLocale } from '../../../lib/i18n';
import { SAMPLE_LISTING_SLUGS } from '../../../lib/sampleListing';
import { getCalendarRanges, isRangeAvailable } from '../../../lib/calendar';

const MAX_IMAGES = 6;
const MAX_IMAGE_BYTES = 5 * 1024 * 1024; // 5 MB per image
const SAMPLE_EMAIL = 'host@lomavuokraus.fi';

function normalizeEvCharging(input?: string | null): EvCharging {
  const value = String(input ?? 'NONE').toUpperCase();
  if (value === 'FREE') return EvCharging.FREE;
  if (value === 'PAID') return EvCharging.PAID;
  return EvCharging.NONE;
}

function resolveImageUrl(img: { id: string; url: string | null; size: number | null }) {
  if (img.size && img.size > 0) {
    return `/api/images/${img.id}`;
  }
  return img.url ?? null;
}

function pickTranslation<T extends { locale: string }>(translations: T[], locale: string | null): T | null {
  if (!translations.length) return null;
  if (locale) {
    const exact = translations.find((t) => t.locale === locale);
    if (exact) return exact;
  }
  return translations[0];
}

function normalizeCalendarUrls(input: unknown): string[] {
  if (Array.isArray(input)) {
    return input
      .map((u) => (typeof u === 'string' ? u.trim() : ''))
      .filter(Boolean)
      .slice(0, 5);
  }
  if (typeof input === 'string') {
    return input
      .split(/\n|,/)
      .map((u) => u.trim())
      .filter(Boolean)
      .slice(0, 5);
  }
  return [];
}

export async function GET(req: Request) {
  const url = new URL(req.url);
  const searchParams = url.searchParams;
  const q = searchParams.get('q')?.trim();
  const city = searchParams.get('city')?.trim();
  const region = searchParams.get('region')?.trim();
  const evChargingParam = searchParams.get('evCharging');
  const evCharging = evChargingParam ? normalizeEvCharging(evChargingParam) : null;
  const startDateParam = searchParams.get('availableStart');
  const endDateParam = searchParams.get('availableEnd');
  const startDate = startDateParam ? new Date(startDateParam) : null;
  const endDate = endDateParam ? new Date(endDateParam) : null;
  const availabilityFilterActive = Boolean(startDate && endDate && startDate < endDate);
  const amenityFilters = searchParams.getAll('amenity').map((a) => a.trim().toLowerCase());
  const locale = resolveLocale({ cookieLocale: null, acceptLanguage: req.headers.get('accept-language') });
  const limit = Math.min(Number(searchParams.get('limit') ?? 40), 100);

  const amenityWhere: Prisma.ListingWhereInput = {};
  if (amenityFilters.includes('sauna')) amenityWhere.hasSauna = true;
  if (amenityFilters.includes('fireplace')) amenityWhere.hasFireplace = true;
  if (amenityFilters.includes('wifi')) amenityWhere.hasWifi = true;
  if (amenityFilters.includes('pets')) amenityWhere.petsAllowed = true;
  if (amenityFilters.includes('lake')) amenityWhere.byTheLake = true;
  if (amenityFilters.includes('ac')) amenityWhere.hasAirConditioning = true;
  if (amenityFilters.includes('kitchen')) amenityWhere.hasKitchen = true;
  if (amenityFilters.includes('dishwasher')) amenityWhere.hasDishwasher = true;
  if (amenityFilters.includes('washer')) amenityWhere.hasWashingMachine = true;
  if (amenityFilters.includes('barbecue')) amenityWhere.hasBarbecue = true;

  const where: Prisma.ListingWhereInput = {
    status: ListingStatus.PUBLISHED,
    removedAt: null,
    city: city ? { contains: city, mode: 'insensitive' } : undefined,
    region: region ? { contains: region, mode: 'insensitive' } : undefined,
    evCharging: evCharging ?? undefined,
    ...amenityWhere,
    translations: q
      ? {
          some: {
            OR: [
              { title: { contains: q, mode: 'insensitive' } },
              { description: { contains: q, mode: 'insensitive' } },
              { teaser: { contains: q, mode: 'insensitive' } },
            ],
          },
        }
      : undefined,
  };

  const listings = await prisma.listing.findMany({
    where,
    include: {
      translations: { select: { id: true, locale: true, title: true, slug: true, teaser: true, description: true } },
      images: { select: { id: true, url: true, altText: true, order: true, isCover: true, size: true }, orderBy: { order: 'asc' } },
    },
    orderBy: { createdAt: 'desc' },
    take: Number.isNaN(limit) ? 40 : limit,
  });

  let filteredListings = listings;
  let availabilityMap = new Map<string, boolean>();

  if (availabilityFilterActive) {
    const checks = await Promise.all(
      listings.map(async (listing) => {
        const urls = listing.calendarUrls ?? [];
        if (!urls.length) return { id: listing.id, available: false };
        const ranges = await getCalendarRanges(urls);
        const available = isRangeAvailable(ranges, startDate!, endDate!);
        return { id: listing.id, available };
      }),
    );
    availabilityMap = new Map(checks.map((c) => [c.id, c.available]));
    filteredListings = listings.filter((l) => availabilityMap.get(l.id));
  }

  const payload = filteredListings.map((listing) => {
    const isSample =
      listing.isSample ||
      listing.contactEmail === SAMPLE_EMAIL ||
      SAMPLE_LISTING_SLUGS.includes(
        pickTranslation(listing.translations, locale)?.slug ?? listing.translations[0]?.slug ?? '',
      );
    const translation = pickTranslation(listing.translations, locale);
    const fallback = listing.translations[0];
    return {
      id: listing.id,
      title: translation?.title ?? fallback?.title ?? 'Listing',
      slug: translation?.slug ?? fallback?.slug ?? '',
      teaser: translation?.teaser ?? translation?.description ?? fallback?.description ?? null,
      locale: translation?.locale ?? fallback?.locale ?? locale,
      country: listing.country,
      region: listing.region,
      city: listing.city,
      streetAddress: listing.streetAddress,
      addressNote: listing.addressNote,
      latitude: listing.latitude,
      longitude: listing.longitude,
      hasSauna: listing.hasSauna,
      hasFireplace: listing.hasFireplace,
      hasWifi: listing.hasWifi,
      petsAllowed: listing.petsAllowed,
      byTheLake: listing.byTheLake,
      hasAirConditioning: listing.hasAirConditioning,
      hasKitchen: listing.hasKitchen,
      hasDishwasher: listing.hasDishwasher,
      hasWashingMachine: listing.hasWashingMachine,
      hasBarbecue: listing.hasBarbecue,
      evCharging: listing.evCharging,
      maxGuests: listing.maxGuests,
      bedrooms: listing.bedrooms,
      beds: listing.beds,
      bathrooms: listing.bathrooms,
      priceHintPerNightEuros: listing.priceHintPerNightEuros,
      coverImage: resolveImageUrl(listing.images.find((img) => img.isCover) ?? listing.images[0] ?? { id: '', url: null, size: null }),
      isSample,
      hasCalendar: Boolean(listing.calendarUrls?.length),
      availableForDates: availabilityFilterActive ? Boolean(availabilityMap.get(listing.id)) : undefined,
    };
  });

  return NextResponse.json({ listings: payload });
}

export async function POST(req: Request) {
  try {
    const auth = await requireAuth(req);
    const user = await prisma.user.findUnique({ where: { id: auth.userId } });
    if (!user || !user.emailVerifiedAt || !user.approvedAt || user.status !== UserStatus.ACTIVE) {
      return NextResponse.json({ error: 'User not permitted to create listings' }, { status: 403 });
    }

    const body = await req.json();
    const slug = String(body.slug ?? '').trim().toLowerCase();
    const locale = String(body.locale ?? 'en').toLowerCase();
    const title = String(body.title ?? '').trim();
    const description = String(body.description ?? '').trim();
    const country = String(body.country ?? '').trim();
    const region = String(body.region ?? '').trim();
    const city = String(body.city ?? '').trim();
    const streetAddress = String(body.streetAddress ?? '').trim();
    const contactName = String(body.contactName ?? '').trim();
    const contactEmail = String(body.contactEmail ?? '').trim();

    if (!slug || !title || !description || !country || !region || !city || !contactEmail || !contactName) {
      return NextResponse.json({ error: 'Missing required fields' }, { status: 400 });
    }

    const maxGuests = Number(body.maxGuests ?? 1);
    const bedrooms = Number(body.bedrooms ?? 1);
    const beds = Number(body.beds ?? 1);
    const bathrooms = Number(body.bathrooms ?? 1);
    const priceHintPerNightEuros = body.priceHintPerNightEuros !== undefined && body.priceHintPerNightEuros !== null && body.priceHintPerNightEuros !== '' ? Math.round(Number(body.priceHintPerNightEuros)) : null;
    const calendarUrls = normalizeCalendarUrls(body.calendarUrls);

    const images = Array.isArray(body.images) ? body.images.slice(0, MAX_IMAGES) : [];
    if (Array.isArray(body.images) && body.images.length > MAX_IMAGES) {
      return NextResponse.json({ error: `Too many images (max ${MAX_IMAGES})` }, { status: 400 });
    }
    const coverImageIndex = Math.min(Math.max(Number(body.coverImageIndex ?? 1), 1), images.length || 1);

    const parsedImages: {
      data?: Buffer;
      mimeType?: string | null;
      size?: number | null;
      url?: string | null;
      altText?: string | null;
      order: number;
      isCover: boolean;
    }[] = [];

    for (let idx = 0; idx < images.length; idx += 1) {
      const img = images[idx];
      const altText = typeof img.altText === 'string' && img.altText.trim() ? img.altText.trim() : null;
      const rawMime = typeof img.mimeType === 'string' ? img.mimeType : null;
      const rawData = typeof img.data === 'string' ? img.data : null;
      const rawUrl = typeof img.url === 'string' && img.url.trim() ? img.url.trim() : null;
      let mimeType = rawMime;
      let buffer: Buffer | null = null;

      if (rawData) {
        const dataUrlMatch = rawData.match(/^data:(.*?);base64,(.*)$/);
        if (dataUrlMatch) {
          mimeType = mimeType || dataUrlMatch[1] || null;
          buffer = Buffer.from(dataUrlMatch[2], 'base64');
        } else {
          buffer = Buffer.from(rawData, 'base64');
        }
      }

      const size = buffer ? buffer.length : null;
      if (size && size > MAX_IMAGE_BYTES) {
        return NextResponse.json({ error: `Image ${idx + 1} is too large (max ${Math.floor(MAX_IMAGE_BYTES / 1024 / 1024)}MB)` }, { status: 400 });
      }

      if (!buffer && !rawUrl) {
        continue;
      }

      parsedImages.push({
        data: buffer ?? undefined,
        mimeType: mimeType || 'image/jpeg',
        size,
        url: buffer ? null : rawUrl,
        altText,
        order: idx + 1,
        isCover: coverImageIndex === idx + 1,
      });
    }

    if (parsedImages.length && !parsedImages.some((img) => img.isCover)) {
      parsedImages[0].isCover = true;
    }

    const autoApprove = process.env.AUTO_APPROVE_LISTINGS === 'true' || auth.role === 'ADMIN';
    const status = autoApprove ? ListingStatus.PUBLISHED : ListingStatus.PENDING;
    const isSample = contactEmail.toLowerCase() === SAMPLE_EMAIL;

    const listing = await prisma.listing.create({
      data: {
        ownerId: user.id,
        status,
        approvedAt: autoApprove ? new Date() : null,
        approvedById: autoApprove && auth.role === 'ADMIN' ? user.id : null,
        country,
        region,
        city,
        streetAddress: streetAddress || null,
        addressNote: body.addressNote ?? null,
        latitude: body.latitude !== undefined && body.latitude !== null && body.latitude !== '' ? Number(body.latitude) : null,
        longitude: body.longitude !== undefined && body.longitude !== null && body.longitude !== '' ? Number(body.longitude) : null,
        maxGuests,
        bedrooms,
        beds,
        bathrooms,
        hasSauna: Boolean(body.hasSauna),
        hasFireplace: Boolean(body.hasFireplace),
        hasWifi: Boolean(body.hasWifi),
        petsAllowed: Boolean(body.petsAllowed),
        byTheLake: Boolean(body.byTheLake),
        hasAirConditioning: Boolean(body.hasAirConditioning),
        hasKitchen: Boolean(body.hasKitchen),
        hasDishwasher: Boolean(body.hasDishwasher),
        hasWashingMachine: Boolean(body.hasWashingMachine),
        hasBarbecue: Boolean(body.hasBarbecue),
        evCharging: normalizeEvCharging(body.evCharging),
        priceHintPerNightEuros,
        calendarUrls,
        contactName,
        contactEmail,
        contactPhone: body.contactPhone ?? null,
        externalUrl: body.externalUrl ?? null,
        published: status === ListingStatus.PUBLISHED,
        isSample,
        translations: {
          create: {
            locale,
            slug,
            title,
            description,
            teaser: body.teaser ?? null,
          },
        },
        images: parsedImages.length
          ? {
              create: parsedImages,
            }
          : undefined,
      },
      include: { translations: true, images: { select: { id: true, altText: true, order: true, isCover: true, size: true, url: true } } },
    });

    return NextResponse.json({ ok: true, listing });
  } catch (error: any) {
    console.error('Create listing error', error);
    const message = error?.code === 'P2002' ? 'Slug already exists for this locale' : 'Failed to create listing';
    return NextResponse.json({ error: message }, { status: 400 });
  }
}
