/**
 * GET /trending
 *
 * Design intent:
 * - Trending reflects calm resonance, not excitement.
 * - Nothing trends forever.
 * - Categories do not compete.
 *
 * Implementation:
 * - Category-scoped lists only (no global trending)
 * - Eligibility: Positive or Neutral tone, High CIS, Low block/report rate
 * - Rank by calm velocity (steady appreciation > spikes)
 * - Allow admin editorial override with expiration
 */

import { serve } from 'https://deno.land/std@0.177.0/http/server.ts';
import { createSupabaseClient, createServiceClient } from '../_shared/supabase-client.ts';
import { rankPosts, type PostForRanking } from '../_shared/ranking.ts';

serve(async (req) => {
  if (req.method === 'OPTIONS') {
    return new Response(null, {
      headers: {
        'Access-Control-Allow-Origin': '*',
        'Access-Control-Allow-Methods': 'GET',
        'Access-Control-Allow-Headers': 'authorization, x-client-info, apikey, content-type',
      },
    });
  }

  try {
    // 1. Validate auth
    const authHeader = req.headers.get('Authorization');
    if (!authHeader) {
      return new Response(JSON.stringify({ error: 'Missing authorization header' }), {
        status: 401,
        headers: { 'Content-Type': 'application/json' },
      });
    }

    const supabase = createSupabaseClient(authHeader);
    const serviceClient = createServiceClient();

    const {
      data: { user },
      error: authError,
    } = await supabase.auth.getUser();

    if (authError || !user) {
      return new Response(JSON.stringify({ error: 'Unauthorized' }), {
        status: 401,
        headers: { 'Content-Type': 'application/json' },
      });
    }

    // 2. Parse query params
    const url = new URL(req.url);
    const categorySlug = url.searchParams.get('category');
    const limit = Math.min(parseInt(url.searchParams.get('limit') || '20'), 50);

    if (!categorySlug) {
      return new Response(
        JSON.stringify({
          error: 'Missing category',
          message: 'Trending is category-scoped. Provide a category slug.',
        }),
        {
          status: 400,
          headers: { 'Content-Type': 'application/json' },
        }
      );
    }

    // 3. Get category ID
    const { data: category, error: categoryError } = await supabase
      .from('categories')
      .select('id, name, slug')
      .eq('slug', categorySlug)
      .single();

    if (categoryError || !category) {
      return new Response(JSON.stringify({ error: 'Category not found' }), {
        status: 404,
        headers: { 'Content-Type': 'application/json' },
      });
    }

    // 4. Check for editorial overrides (unexpired)
    const { data: overrides } = await supabase
      .from('trending_overrides')
      .select(
        `
        post_id,
        reason,
        posts (
          id,
          body,
          created_at,
          tone_label,
          cis_score,
          allow_chain,
          chain_parent_id,
          chain_parent:posts!posts_chain_parent_id_fkey (
            id,
            body,
            created_at,
            author:profiles!posts_author_id_fkey (
              id,
              handle,
              display_name,
              avatar_url
            )
          ),
          author:profiles!posts_author_id_fkey (
            id,
            handle,
            display_name,
            avatar_url
          ),
          category:categories!posts_category_id_fkey (
            id,
            slug,
            name
          ),
          metrics:post_metrics (
            like_count,
            save_count
          )
        )
      `
      )
      .eq('category_id', category.id)
      .gt('expires_at', new Date().toISOString())
      .order('created_at', { ascending: false });

    const overridePosts =
      overrides?.map((o: any) => ({
        ...o.posts,
        is_editorial: true,
        editorial_reason: o.reason,
      })) || [];

    // 5. Fetch candidate posts for algorithmic trending
    // Eligibility:
    // - Positive or Neutral tone only
    // - CIS >= 0.8 (high content integrity)
    // - Created in last 48 hours (trending is recent)
    // - Active status
    const twoDaysAgo = new Date(Date.now() - 48 * 60 * 60 * 1000).toISOString();

    const { data: posts, error: postsError } = await serviceClient
      .from('posts')
      .select(
        `
        id,
        body,
        created_at,
        category_id,
        tone_label,
        cis_score,
        author_id,
        author:profiles!posts_author_id_fkey (
          id,
          handle,
          display_name,
          avatar_url
        ),
        category:categories!posts_category_id_fkey (
          id,
          slug,
          name
        ),
        metrics:post_metrics (
          like_count,
          save_count,
          view_count
        )
      `
      )
      .eq('category_id', category.id)
      .in('tone_label', ['positive', 'neutral'])
      .gte('cis_score', 0.8)
      .gte('created_at', twoDaysAgo)
      .eq('status', 'active')
      .limit(100); // Candidate pool

    if (postsError) {
      console.error('Error fetching trending posts:', postsError);
      return new Response(JSON.stringify({ error: 'Failed to fetch trending posts' }), {
        status: 500,
        headers: { 'Content-Type': 'application/json' },
      });
    }

    // 6. Enrich posts with safety metrics
    const authorIds = [...new Set(posts.map((p) => p.author_id))];

    const { data: trustStates } = await serviceClient
      .from('trust_state')
      .select('user_id, harmony_score, tier')
      .in('user_id', authorIds);

    const trustMap = new Map(trustStates?.map((t) => [t.user_id, t]) || []);

    const oneDayAgo = new Date(Date.now() - 24 * 60 * 60 * 1000).toISOString();

    const { data: recentBlocks } = await serviceClient
      .from('blocks')
      .select('blocked_id')
      .in('blocked_id', authorIds)
      .gte('created_at', oneDayAgo);

    const blocksMap = new Map<string, number>();
    recentBlocks?.forEach((block) => {
      blocksMap.set(block.blocked_id, (blocksMap.get(block.blocked_id) || 0) + 1);
    });

    const postIds = posts.map((p) => p.id);

    const { data: reports } = await serviceClient
      .from('reports')
      .select('target_id, reporter_id')
      .eq('target_type', 'post')
      .in('target_id', postIds);

    const trustedReportMap = new Map<string, number>();
    const totalReportMap = new Map<string, number>();

    for (const report of reports || []) {
      totalReportMap.set(report.target_id, (totalReportMap.get(report.target_id) || 0) + 1);

      const reporterTrust = trustMap.get(report.reporter_id);
      if (reporterTrust && reporterTrust.harmony_score >= 70) {
        trustedReportMap.set(report.target_id, (trustedReportMap.get(report.target_id) || 0) + 1);
      }
    }

    // 7. Filter out posts with safety issues
    const safePosts = posts.filter((post) => {
      const blocksReceived = blocksMap.get(post.author_id) || 0;
      const trustedReports = trustedReportMap.get(post.id) || 0;

      // Exclude if:
      // - Author received 2+ blocks in 24h
      // - Post has any trusted reports
      return blocksReceived < 2 && trustedReports === 0;
    });

    // 8. Transform and rank
    const postsForRanking: PostForRanking[] = safePosts.map((post) => {
      const authorTrust = trustMap.get(post.author_id);

      return {
        id: post.id,
        created_at: post.created_at,
        cis_score: post.cis_score || 0.8,
        tone_label: post.tone_label || 'neutral',
        save_count: post.metrics?.save_count || 0,
        like_count: post.metrics?.like_count || 0,
        view_count: post.metrics?.view_count || 0,
        author_harmony_score: authorTrust?.harmony_score || 50,
        author_tier: authorTrust?.tier || 'new',
        blocks_received_24h: blocksMap.get(post.author_id) || 0,
        trusted_reports: trustedReportMap.get(post.id) || 0,
        total_reports: totalReportMap.get(post.id) || 0,
      };
    });

    const rankedPosts = rankPosts(postsForRanking);

    // 9. Take top N algorithmic posts
    const topPosts = rankedPosts.slice(0, limit - overridePosts.length);

    // 10. Fetch full data for algorithmic posts
    const algorithmicIds = topPosts.map((p) => p.id);

    const { data: algorithmicPosts } = await supabase
      .from('posts')
      .select(
        `
        id,
        body,
        created_at,
        tone_label,
        allow_chain,
        chain_parent_id,
        chain_parent:posts!posts_chain_parent_id_fkey (
          id,
          body,
          created_at,
          author:profiles!posts_author_id_fkey (
            id,
            handle,
            display_name,
            avatar_url
          )
        ),
        author:profiles!posts_author_id_fkey (
          id,
          handle,
          display_name,
          avatar_url
        ),
        category:categories!posts_category_id_fkey (
          id,
          slug,
          name
        ),
        metrics:post_metrics (
          like_count,
          save_count
        )
      `
      )
      .in('id', algorithmicIds);

    const algorithmicWithFlag = algorithmicPosts?.map((p) => ({ ...p, is_editorial: false })) || [];

    // 11. Merge editorial overrides first, then algorithmic
    const trendingPosts = [...overridePosts, ...algorithmicWithFlag];

    return new Response(
      JSON.stringify({
        category: {
          id: category.id,
          slug: category.slug,
          name: category.name,
        },
        posts: trendingPosts,
        explanation:
          'Trending shows calm resonance: steady saves and appreciation from trusted accounts. Editorial picks are marked. Nothing trends forever.',
      }),
      {
        status: 200,
        headers: { 'Content-Type': 'application/json' },
      }
    );
  } catch (error) {
    console.error('Unexpected error:', error);
    return new Response(JSON.stringify({ error: 'Internal server error' }), {
      status: 500,
      headers: { 'Content-Type': 'application/json' },
    });
  }
});
