import { NextResponse } from 'next/server';
import { ListingStatus, UserStatus, 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 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];
}

export 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 function parsePrice(value: unknown): number | null {
  if (value === undefined || value === null || value === '') return null;
  const num = Number(value);
  if (Number.isNaN(num)) return null;
  return Math.round(num);
}

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 === 'true' ? true : evChargingParam === 'false' ? false : null;
  const evChargingOnSiteParam = searchParams.get('evChargingOnSite');
  const evChargingOnSite = evChargingOnSiteParam === 'true' ? true : evChargingOnSiteParam === 'false' ? false : 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;
  if (amenityFilters.includes('microwave')) amenityWhere.hasMicrowave = true;
  if (amenityFilters.includes('parking')) amenityWhere.hasFreeParking = true;
  if (amenityFilters.includes('skipass')) amenityWhere.hasSkiPass = true;
  if (amenityFilters.includes('accessible')) amenityWhere.wheelchairAccessible = true;
  if (amenityFilters.includes('ev-onsite')) amenityWhere.evChargingOnSite = true;

  const where: Prisma.ListingWhereInput = {
    status: ListingStatus.PUBLISHED,
    removedAt: null,
    city: city ? { contains: city, mode: 'insensitive' } : undefined,
    region: region ? { contains: region, mode: 'insensitive' } : undefined,
    evChargingAvailable: evCharging ?? undefined,
    evChargingOnSite: evChargingOnSite ?? 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];
    const validCalendarUrls = (listing.calendarUrls ?? []).filter((url) => {
      if (!url) return false;
      try {
        const parsed = new URL(url);
        return parsed.protocol === 'http:' || parsed.protocol === 'https:';
      } catch {
        return false;
      }
    });
    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,
      hasMicrowave: listing.hasMicrowave,
      hasFreeParking: listing.hasFreeParking,
      hasSkiPass: listing.hasSkiPass,
      evChargingAvailable: listing.evChargingAvailable,
      evChargingOnSite: listing.evChargingOnSite,
      wheelchairAccessible: listing.wheelchairAccessible,
      maxGuests: listing.maxGuests,
      bedrooms: listing.bedrooms,
      beds: listing.beds,
      bathrooms: listing.bathrooms,
      priceWeekdayEuros: listing.priceWeekdayEuros,
      priceWeekendEuros: listing.priceWeekendEuros,
      coverImage: resolveImageUrl(listing.images.find((img) => img.isCover) ?? listing.images[0] ?? { id: '', url: null, size: null }),
      isSample,
      hasCalendar: Boolean(validCalendarUrls.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 saveDraft = Boolean(body.saveDraft);
    const slug = String(body.slug ?? '').trim().toLowerCase();
    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) {
      return NextResponse.json({ error: 'Missing slug' }, { status: 400 });
    }

    const maxGuests = body.maxGuests === undefined || body.maxGuests === null || body.maxGuests === '' ? null : Number(body.maxGuests);
    const bedrooms = body.bedrooms === undefined || body.bedrooms === null || body.bedrooms === '' ? null : Number(body.bedrooms);
    const beds = body.beds === undefined || body.beds === null || body.beds === '' ? null : Number(body.beds);
    const bathrooms = body.bathrooms === undefined || body.bathrooms === null || body.bathrooms === '' ? null : Number(body.bathrooms);
    const priceWeekdayEuros = parsePrice(body.priceWeekdayEuros);
    const priceWeekendEuros = parsePrice(body.priceWeekendEuros);
    const calendarUrls = normalizeCalendarUrls(body.calendarUrls);
    const translationsInputRaw = Array.isArray(body.translations) ? body.translations : [];
    type TranslationInput = { locale: string; title: string; description: string; teaser: string | null; slug: string };
    let translationsInput =
      translationsInputRaw
        .map((item: any) => ({
          locale: String(item.locale ?? '').toLowerCase(),
          title: typeof item.title === 'string' ? item.title.trim() : '',
          description: typeof item.description === 'string' ? item.description.trim() : '',
          teaser: typeof item.teaser === 'string' ? item.teaser.trim() : null,
          slug: String(item.slug ?? slug).trim().toLowerCase(),
        }))
        .filter((t: any) => t.locale && (saveDraft || (t.title && t.description))) || [];

    const fallbackLocale = String(body.locale ?? 'en').toLowerCase();
    const fallbackTranslationTitle = typeof body.title === 'string' ? body.title.trim() : '';
    const fallbackTranslationDescription = typeof body.description === 'string' ? body.description.trim() : '';
    const fallbackTranslationTeaser = typeof body.teaser === 'string' ? body.teaser.trim() : null;

    if (translationsInput.length === 0 && (fallbackTranslationTitle || saveDraft) && (fallbackTranslationDescription || saveDraft)) {
      translationsInput.push({
        locale: fallbackLocale,
        title: fallbackTranslationTitle ?? '',
        description: fallbackTranslationDescription ?? '',
        teaser: fallbackTranslationTeaser,
        slug,
      });
    }

    if (!translationsInput.length && !saveDraft) {
      return NextResponse.json({ error: 'Missing translation fields (title/description)' }, { status: 400 });
    }

    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;
    }

    if (!saveDraft) {
      const missingFields: string[] = [];
      if (!country) missingFields.push('country');
      if (!region) missingFields.push('region');
      if (!city) missingFields.push('city');
      if (!contactEmail) missingFields.push('contactEmail');
      if (!contactName) missingFields.push('contactName');
      if (!maxGuests) missingFields.push('maxGuests');
      if (!bedrooms && bedrooms !== 0) missingFields.push('bedrooms');
      if (!beds) missingFields.push('beds');
      if (!bathrooms) missingFields.push('bathrooms');
      if (!translationsInput.length) missingFields.push('translations');
      if (!parsedImages.length) missingFields.push('images');
      if (missingFields.length) {
        return NextResponse.json({ error: `Missing required fields: ${missingFields.join(', ')}` }, { status: 400 });
      }
    }

    const autoApprove = !saveDraft && (process.env.AUTO_APPROVE_LISTINGS === 'true' || auth.role === 'ADMIN');
    const status = saveDraft ? ListingStatus.DRAFT : autoApprove ? ListingStatus.PUBLISHED : ListingStatus.PENDING;
    const isSample = (contactEmail || '').toLowerCase() === SAMPLE_EMAIL;
    const evChargingOnSite = Boolean(body.evChargingOnSite);
    const evChargingAvailable = Boolean(body.evChargingAvailable) || evChargingOnSite;
    const wheelchairAccessible = Boolean(body.wheelchairAccessible);

    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: country || null,
        region: region || null,
        city: city || null,
        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),
        hasMicrowave: Boolean(body.hasMicrowave),
        hasFreeParking: Boolean(body.hasFreeParking),
        hasSkiPass: Boolean(body.hasSkiPass),
        evChargingAvailable,
        evChargingOnSite,
        wheelchairAccessible,
        priceWeekdayEuros,
        priceWeekendEuros,
        calendarUrls,
        contactName: contactName || null,
        contactEmail: contactEmail || null,
        contactPhone: body.contactPhone ?? null,
        externalUrl: body.externalUrl ?? null,
        published: status === ListingStatus.PUBLISHED,
        isSample,
        translations: translationsInput.length
          ? {
              create: translationsInput.map((t: TranslationInput) => ({
                locale: t.locale,
                slug: t.slug || slug,
                title: t.title,
                description: t.description,
                teaser: t.teaser ?? null,
              })),
            }
          : undefined,
        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 });
  }
}
