// src/app/(frontend)/api/news/route.ts
// Dedizierte News-API mit erweiterten Features für Frontend-News-Seiten

import { NextRequest, NextResponse } from 'next/server'
import { getPayload } from 'payload'
import type { Where } from 'payload'
import config from '@payload-config'
import type { Category, Media, Post } from '@/payload-types'

type Locale = 'de' | 'en' | 'all'
import {
  searchLimiter,
  rateLimitHeaders,
  getClientIpFromRequest,
  isIpBlocked,
} from '@/lib/security'

// Validation constants
const MAX_LIMIT = 50
const DEFAULT_LIMIT = 12
const NEWS_RATE_LIMIT = 30

// Valid post types for news
const NEWS_TYPES = ['news', 'press', 'announcement', 'blog'] as const
type NewsType = (typeof NEWS_TYPES)[number]

interface NewsQueryParams {
  tenantId?: number
  type?: NewsType | NewsType[]
  categorySlug?: string
  featured?: boolean
  search?: string
  year?: number
  month?: number
  locale: Locale
  page: number
  limit: number
  excludeIds?: number[]
}

async function getNews(payload: Awaited<ReturnType<typeof getPayload>>, params: NewsQueryParams) {
  const {
    tenantId,
    type,
    categorySlug,
    featured,
    search,
    year,
    month,
    locale,
    page,
    limit,
    excludeIds,
  } = params

  // Build where clause
  const where: Where = {
    status: { equals: 'published' },
  }

  // Tenant filter
  if (tenantId) {
    where.tenant = { equals: tenantId }
  }

  // Type filter (single or multiple)
  if (type) {
    if (Array.isArray(type)) {
      where.type = { in: type }
    } else {
      where.type = { equals: type }
    }
  }

  // Featured filter
  if (featured !== undefined) {
    where.isFeatured = { equals: featured }
  }

  // Search filter
  if (search) {
    where.or = [
      { title: { contains: search } },
      { excerpt: { contains: search } },
    ]
  }

  // Date filters
  if (year) {
    const startDate = new Date(year, month ? month - 1 : 0, 1)
    const endDate = month
      ? new Date(year, month, 0, 23, 59, 59) // Last day of month
      : new Date(year, 11, 31, 23, 59, 59) // Last day of year

    where.publishedAt = {
      greater_than_equal: startDate.toISOString(),
      less_than_equal: endDate.toISOString(),
    }
  }

  // Exclude specific IDs (useful for "more articles" sections)
  if (excludeIds && excludeIds.length > 0) {
    where.id = { not_in: excludeIds }
  }

  // Category filter via join
  let categoryId: number | undefined
  if (categorySlug) {
    const categoryResult = await payload.find({
      collection: 'categories',
      where: {
        slug: { equals: categorySlug },
        ...(tenantId ? { tenant: { equals: tenantId } } : {}),
      },
      limit: 1,
      locale,
    })
    if (categoryResult.docs.length > 0) {
      categoryId = categoryResult.docs[0].id
      where.categories = { contains: categoryId }
    } else {
      // Category not found - return empty result
      return {
        docs: [],
        page: 1,
        totalPages: 0,
        totalDocs: 0,
        hasNextPage: false,
        hasPrevPage: false,
      }
    }
  }

  // Execute query
  return payload.find({
    collection: 'posts',
    where: where as Where,
    sort: '-publishedAt',
    page,
    limit,
    locale,
    depth: 2, // Load relations (categories, media)
  })
}

// Helper to get available categories for a tenant
async function getCategories(
  payload: Awaited<ReturnType<typeof getPayload>>,
  tenantId?: number,
  locale: Locale = 'de'
) {
  const where: Where = {}
  if (tenantId) {
    where.tenant = { equals: tenantId }
  }

  const result = await payload.find({
    collection: 'categories',
    where: where as Where,
    sort: 'name',
    limit: 100,
    locale,
  })

  return result.docs.map((cat) => ({
    id: cat.id,
    name: cat.name,
    slug: cat.slug,
  }))
}

// Helper to get archive data (years/months with post counts)
// Uses pagination to handle large datasets without memory issues
async function getArchive(
  payload: Awaited<ReturnType<typeof getPayload>>,
  tenantId: number // Now required
) {
  const where: Where = {
    status: { equals: 'published' },
    publishedAt: { exists: true },
    tenant: { equals: tenantId },
  }

  // Group by year and month using pagination
  const archive: Record<number, Record<number, number>> = {}
  let page = 1
  const pageSize = 500
  let hasMore = true

  while (hasMore) {
    const result = await payload.find({
      collection: 'posts',
      where: where as Where,
      sort: '-publishedAt',
      page,
      limit: pageSize,
      depth: 0,
    })

    for (const post of result.docs) {
      if (post.publishedAt) {
        const date = new Date(post.publishedAt)
        const year = date.getFullYear()
        const month = date.getMonth() + 1

        if (!archive[year]) {
          archive[year] = {}
        }
        archive[year][month] = (archive[year][month] || 0) + 1
      }
    }

    hasMore = result.hasNextPage
    page++

    // Safety limit: max 20 pages (10,000 posts)
    if (page > 20) break
  }

  // Transform to sorted array
  return Object.entries(archive)
    .sort(([a], [b]) => Number(b) - Number(a))
    .map(([year, months]) => ({
      year: Number(year),
      months: Object.entries(months)
        .sort(([a], [b]) => Number(b) - Number(a))
        .map(([month, count]) => ({
          month: Number(month),
          count,
        })),
      total: Object.values(months).reduce((sum, count) => sum + count, 0),
    }))
}

export async function GET(request: NextRequest) {
  try {
    // IP-Blocklist prüfen
    const ip = getClientIpFromRequest(request)
    if (isIpBlocked(ip)) {
      return NextResponse.json({ error: 'Access denied' }, { status: 403 })
    }

    // Rate limiting
    const rateLimit = await searchLimiter.check(ip)
    if (!rateLimit.allowed) {
      return NextResponse.json(
        { error: 'Too many requests. Please try again later.' },
        { status: 429, headers: rateLimitHeaders(rateLimit, NEWS_RATE_LIMIT) }
      )
    }

    // Parse query parameters
    const { searchParams } = new URL(request.url)

    // Basic params
    const tenantParam = searchParams.get('tenant')
    const pageParam = searchParams.get('page')
    const limitParam = searchParams.get('limit')
    const localeParam = searchParams.get('locale')?.trim()

    // Filter params
    const typeParam = searchParams.get('type')?.trim()
    const typesParam = searchParams.get('types')?.trim() // Comma-separated for multiple
    const categoryParam = searchParams.get('category')?.trim()
    const featuredParam = searchParams.get('featured')
    const searchParam = searchParams.get('search')?.trim()
    const yearParam = searchParams.get('year')
    const monthParam = searchParams.get('month')
    const excludeParam = searchParams.get('exclude')?.trim() // Comma-separated IDs

    // Meta params
    const includeCategories = searchParams.get('includeCategories') === 'true'
    const includeArchive = searchParams.get('includeArchive') === 'true'

    // Validate locale
    const validLocales: Locale[] = ['de', 'en']
    const locale: Locale = localeParam && validLocales.includes(localeParam as Locale) ? (localeParam as Locale) : 'de'

    // Validate and parse types
    let types: NewsType | NewsType[] | undefined
    if (typesParam) {
      const typeList = typesParam.split(',').map((t) => t.trim()) as NewsType[]
      const invalidTypes = typeList.filter((t) => !NEWS_TYPES.includes(t))
      if (invalidTypes.length > 0) {
        return NextResponse.json(
          { error: `Invalid types: ${invalidTypes.join(', ')}. Valid: ${NEWS_TYPES.join(', ')}` },
          { status: 400 }
        )
      }
      types = typeList
    } else if (typeParam) {
      if (!NEWS_TYPES.includes(typeParam as NewsType)) {
        return NextResponse.json(
          { error: `Invalid type. Must be one of: ${NEWS_TYPES.join(', ')}` },
          { status: 400 }
        )
      }
      types = typeParam as NewsType
    }

    // Parse numeric parameters
    const page = Math.max(1, parseInt(pageParam || '1', 10) || 1)
    const limit = Math.min(
      Math.max(1, parseInt(limitParam || String(DEFAULT_LIMIT), 10) || DEFAULT_LIMIT),
      MAX_LIMIT
    )
    const tenantId = tenantParam ? parseInt(tenantParam, 10) : undefined
    const year = yearParam ? parseInt(yearParam, 10) : undefined
    const month = monthParam ? parseInt(monthParam, 10) : undefined

    // Validate tenant ID - REQUIRED for tenant isolation
    if (!tenantParam) {
      return NextResponse.json(
        { error: 'Tenant ID is required. Use ?tenant=<id> to specify the tenant.' },
        { status: 400 }
      )
    }
    if (isNaN(tenantId!) || tenantId! < 1) {
      return NextResponse.json({ error: 'Invalid tenant ID' }, { status: 400 })
    }

    // Validate year/month
    if (year && (year < 2000 || year > 2100)) {
      return NextResponse.json({ error: 'Invalid year' }, { status: 400 })
    }
    if (month && (month < 1 || month > 12)) {
      return NextResponse.json({ error: 'Invalid month (1-12)' }, { status: 400 })
    }

    // Parse featured
    const featured = featuredParam === 'true' ? true : featuredParam === 'false' ? false : undefined

    // Parse exclude IDs
    const excludeIds = excludeParam
      ? excludeParam.split(',').map((id) => parseInt(id.trim(), 10)).filter((id) => !isNaN(id))
      : undefined

    // Get payload instance
    const payload = await getPayload({ config })

    // Execute main query
    const result = await getNews(payload, {
      tenantId,
      type: types,
      categorySlug: categoryParam,
      featured,
      search: searchParam,
      year,
      month,
      locale,
      page,
      limit,
      excludeIds,
    })

    // Transform posts
    const transformedDocs = result.docs.map((post: Post) => {
      const featuredImage = post.featuredImage as Media | null
      const categories = (post.categories || []) as Category[]

      return {
        id: post.id,
        title: post.title,
        slug: post.slug,
        type: post.type || 'blog',
        excerpt: post.excerpt || null,
        author: post.author || null,
        publishedAt: post.publishedAt || null,
        isFeatured: post.isFeatured || false,
        featuredImage: featuredImage
          ? {
              url: featuredImage.url,
              alt: featuredImage.alt || post.title,
              width: featuredImage.width,
              height: featuredImage.height,
              // Responsive sizes for frontend
              sizes: featuredImage.sizes || null,
            }
          : null,
        categories: categories
          .filter((cat): cat is Category => cat !== null && typeof cat === 'object' && 'name' in cat)
          .map((cat) => ({
            id: cat.id,
            name: cat.name,
            slug: cat.slug,
          })),
        seo: post.seo
          ? {
              metaTitle: post.seo.metaTitle || post.title,
              metaDescription: post.seo.metaDescription || post.excerpt || null,
            }
          : null,
      }
    })

    // Build response
    const response: Record<string, unknown> = {
      docs: transformedDocs,
      pagination: {
        page: result.page,
        limit,
        totalPages: result.totalPages,
        totalDocs: result.totalDocs,
        hasNextPage: result.hasNextPage,
        hasPrevPage: result.hasPrevPage,
      },
      filters: {
        type: types,
        category: categoryParam,
        featured,
        search: searchParam,
        year,
        month,
        locale,
        tenant: tenantId,
      },
    }

    // Include categories if requested
    if (includeCategories) {
      response.categories = await getCategories(payload, tenantId, locale)
    }

    // Include archive if requested
    if (includeArchive) {
      response.archive = await getArchive(payload, tenantId!)
    }

    return NextResponse.json(response, {
      headers: {
        ...rateLimitHeaders(rateLimit, NEWS_RATE_LIMIT),
        'Cache-Control': 'public, max-age=60, s-maxage=120',
      },
    })
  } catch (error) {
    console.error('[News API] Error:', error)
    return NextResponse.json({ error: 'Internal server error' }, { status: 500 })
  }
}
