import { NextRequest, NextResponse } from 'next/server';
import { logger } from '@/lib/logger';
import { constructWebhookEvent, stripe } from '@/lib/stripe/server';
import { getSupabaseAdmin } from '@/lib/supabase/server';
import type Stripe from 'stripe';
import { HTTP_STATUS_INTERNAL_SERVER_ERROR } from '@/lib/constants';
import { sendSms, SMS_TEMPLATES, isValidAuMobile } from '@/lib/sms';
import { sendEmail, EMAIL_TEMPLATES } from '@/lib/email';

// Disable body parsing for webhook verification
export const runtime = 'nodejs';
export const dynamic = 'force-dynamic';

export async function POST(request: NextRequest) {
  try {
    // Get the raw body as text for signature verification
    const payload = await request.text();
    const signature = request.headers.get('stripe-signature');

    if (!signature) {
      return NextResponse.json({ error: 'Missing stripe-signature header' }, { status: 400 });
    }

    // Verify webhook signature and construct event
    let event: Stripe.Event;
    try {
      event = constructWebhookEvent(payload, signature);
    } catch (error) {
      logger.error('Webhook signature verification failed:', error);
      return NextResponse.json({ error: 'Invalid signature' }, { status: 400 });
    }

    const supabaseService = getSupabaseAdmin();

    // Handle specific event types
    switch (event.type) {
      case 'payment_intent.succeeded': {
        const paymentIntent = event.data.object as Stripe.PaymentIntent;
        await handlePaymentSuccess(supabaseService, paymentIntent);
        break;
      }

      case 'payment_intent.payment_failed': {
        const paymentIntent = event.data.object as Stripe.PaymentIntent;
        await handlePaymentFailure(supabaseService, paymentIntent);
        break;
      }

      case 'payment_intent.canceled': {
        const paymentIntent = event.data.object as Stripe.PaymentIntent;
        await handlePaymentCancellation(supabaseService, paymentIntent);
        break;
      }

      case 'checkout.session.completed': {
        const session = event.data.object as Stripe.Checkout.Session;
        if (session.payment_status === 'paid' && session.metadata?.match_id) {
          // Retrieve the PaymentIntent from the session to get full details
          const piId = typeof session.payment_intent === 'string' 
            ? session.payment_intent 
            : session.payment_intent?.id;
          if (piId) {
            const paymentIntent = await stripe.paymentIntents.retrieve(piId);
            await handlePaymentSuccess(supabaseService, paymentIntent);
          }
        }
        break;
      }

      default:
        // Ignore unhandled event types
        break;
    }

    return NextResponse.json({ received: true });
  } catch (error) {
    logger.error('Webhook error:', error);
    return NextResponse.json({ error: 'Webhook processing failed' }, { status: HTTP_STATUS_INTERNAL_SERVER_ERROR });
  }
}

async function handlePaymentSuccess(
  supabase: ReturnType<typeof getSupabaseAdmin>,
  paymentIntent: Stripe.PaymentIntent
) {
  const matchId = paymentIntent.metadata?.match_id;
  
  if (!matchId) {
    logger.error('No match_id in payment intent metadata');
    throw new Error('No match_id in payment intent metadata');
  }

  // Update payment status to charged
  // Try by stripe_payment_id first, then fall back to match_id (Checkout flow uses placeholder)
  let payment: { id: string; match_id: string; company_id: string } | null = null;
  let paymentError: { message: string } | null = null;

  const { data: p1, error: e1 } = await supabase
    .from('payments')
    .update({
      status: 'charged',
      stripe_payment_id: paymentIntent.id, // Update to real PI id
    })
    .eq('stripe_payment_id', paymentIntent.id)
    .select('id, match_id, company_id')
    .maybeSingle();

  if (p1) {
    payment = p1;
  } else {
    // Checkout flow: payment record has placeholder stripe_payment_id
    const { data: p2, error: e2 } = await supabase
      .from('payments')
      .update({
        status: 'charged',
        stripe_payment_id: paymentIntent.id,
      })
      .eq('match_id', matchId)
      .eq('status', 'pending')
      .select('id, match_id, company_id')
      .maybeSingle();

    payment = p2;
    paymentError = e2;
  }

  if (!payment) {
    logger.error('Payment not found or update failed for intent:', paymentIntent.id, 'match:', matchId, 'e1:', e1, 'e2:', paymentError);
    throw new Error(`Payment not found for intent: ${paymentIntent.id}`);
  }

  // Update match status to hired (only if not already hired)
  const { error: matchError } = await supabase
    .from('matches')
    .update({
      status: 'hired',
      contractor_action: 'accepted',
      hired_at: new Date().toISOString(),
      fee_charged: true,
    })
    .eq('id', matchId)
    .neq('status', 'hired');

  if (matchError) {
    logger.error('Error updating match status:', matchError);
    throw new Error(`Failed to update match status: ${matchError.message}`);
  }

  // Create notification for worker (non-critical, don't throw)
  const { data: match } = await supabase
    .from('matches')
    .select('worker_id, jobs(title, companies!inner(profile_id))')
    .eq('id', matchId)
    .single();

  if (match) {
    const jobs = match.jobs as { title?: string; company_id?: string; companies?: { profile_id?: string } } | null;
    const jobTitle = jobs?.title || 'a job';
    const contractorProfileId = jobs?.companies?.profile_id;

    // Auto-create conversation between contractor and worker
    // Uses direct inserts instead of RPC (RPC lacks SECURITY DEFINER, RLS blocks service role)
    let conversationId: string | null = null;
    if (contractorProfileId && match.worker_id) {
      try {
        // Check if conversation already exists
        const { data: existingParts } = await supabase
          .from('conversation_participants')
          .select('conversation_id')
          .eq('profile_id', contractorProfileId);

        if (existingParts && existingParts.length > 0) {
          const convIds = existingParts.map((p: { conversation_id: string }) => p.conversation_id);
          const { data: matchingPart } = await supabase
            .from('conversation_participants')
            .select('conversation_id')
            .eq('profile_id', match.worker_id)
            .in('conversation_id', convIds)
            .limit(1)
            .single();

          if (matchingPart) {
            conversationId = matchingPart.conversation_id;
          }
        }

        if (!conversationId) {
          const { data: newConv } = await supabase
            .from('conversations')
            .insert({})
            .select('id')
            .single();

          if (newConv) {
            await supabase
              .from('conversation_participants')
              .insert([
                { conversation_id: newConv.id, profile_id: contractorProfileId },
                { conversation_id: newConv.id, profile_id: match.worker_id },
              ]);
            conversationId = newConv.id;
          }
        }
      } catch (convErr) {
        logger.error('Failed to auto-create conversation on hire:', convErr);
      }
    }

    const { error: notifError } = await supabase.from('notifications').insert({
      profile_id: match.worker_id,
      type: 'hire_confirmed',
      title: 'You\'ve been hired!',
      body: `Congratulations! You've been hired for ${jobTitle}.`,
      data: { match_id: matchId, payment_id: payment.id, ...(conversationId && { conversationId }) },
    });
    if (notifError) {
      logger.error('Failed to create hire notification:', notifError);
    }

    // Send SMS + email notifications (fire-and-forget)
    try {
      const { data: workerProfile } = await supabase
        .from('profiles')
        .select('name, phone, email')
        .eq('id', match.worker_id)
        .single();

      const { data: company } = await supabase
        .from('companies')
        .select('name')
        .eq('profile_id', contractorProfileId)
        .single();

      const companyName = company?.name || 'A contractor';

      // SMS
      if (workerProfile?.phone && isValidAuMobile(workerProfile.phone)) {
        sendSms(workerProfile.phone, SMS_TEMPLATES.workerHired(jobTitle, companyName)).catch((err) =>
          logger.error('Hire SMS failed (webhook):', err)
        );
      }

      // Email
      if (workerProfile?.email) {
        const template = EMAIL_TEMPLATES.workerHired(
          workerProfile.name || 'Worker',
          jobTitle,
          companyName
        );
        sendEmail(workerProfile.email, template.subject, template.html).catch((err) =>
          logger.error('Hire email failed (webhook):', err)
        );
      }

      // Contractor confirmation SMS
      if (contractorProfileId) {
        const { data: contractorProfile } = await supabase
          .from('profiles')
          .select('phone')
          .eq('id', contractorProfileId)
          .single();

        if (contractorProfile?.phone && isValidAuMobile(contractorProfile.phone)) {
          sendSms(
            contractorProfile.phone,
            SMS_TEMPLATES.contractorHireConfirmed(
              jobTitle,
              workerProfile?.name || 'Worker',
              workerProfile?.phone || 'see app'
            )
          ).catch((err) => logger.error('Contractor hire SMS failed (webhook):', err));
        }
      }
    } catch (notifyErr) {
      logger.error('Notification error in webhook:', notifyErr);
    }
  }
}

async function handlePaymentFailure(
  supabase: ReturnType<typeof getSupabaseAdmin>,
  paymentIntent: Stripe.PaymentIntent
) {
  try {
    const { error } = await supabase
      .from('payments')
      .update({
        status: 'pending', // Keep as pending to allow retry
      })
      .eq('stripe_payment_id', paymentIntent.id);

    if (error) {
      logger.error('Error updating payment failure:', error);
    }
  } catch (error) {
    logger.error('Error handling payment failure:', error);
  }
}

async function handlePaymentCancellation(
  supabase: ReturnType<typeof getSupabaseAdmin>,
  paymentIntent: Stripe.PaymentIntent
) {
  try {
    const { error } = await supabase
      .from('payments')
      .delete()
      .eq('stripe_payment_id', paymentIntent.id)
      .eq('status', 'pending');

    if (error) {
      logger.error('Error deleting cancelled payment:', error);
    }
  } catch (error) {
    logger.error('Error handling payment cancellation:', error);
  }
}
