import { NextRequest, NextResponse } from "next/server";
import { logger } from "@/lib/logger";
import { stripe } from "@/lib/stripe/server";
import { createClient, getSupabaseAdmin } from "@/lib/supabase/server";
import { sendSms, SMS_TEMPLATES, isValidAuMobile } from "@/lib/sms";
import { sendEmail, EMAIL_TEMPLATES } from "@/lib/email";
import { checkRateLimit, getClientIP, createRateLimitResponse } from "@/lib/rate-limit";
import { HTTP_STATUS_INTERNAL_SERVER_ERROR } from "@/lib/constants";
import { recordMilestone, recordRepeatMilestone } from "@/lib/success-tracking";

// Rate limit: 5 confirm-hire requests per 5 minutes (prevent abuse)
const RATE_LIMIT = 5;
const WINDOW_MS = 5 * 60 * 1000;

/**
 * POST /api/payments/confirm-hire
 * 
 * Called from the payment success page as a fallback to ensure
 * the match is updated to hired + worker is notified, even if
 * the Stripe webhook hasn't processed yet.
 * 
 * Note: No CSRF protection — called immediately after Stripe redirect
 * where CSRF token may not be available. Protected by session_id
 * verification against Stripe API instead.
 * 
 * Body: { session_id: string, match_id: string }
 */
export async function POST(request: NextRequest) {
  try {
    const clientIP = getClientIP(request);
    const rateLimit = await checkRateLimit(`confirm-hire:${clientIP}`, RATE_LIMIT, WINDOW_MS);
    if (!rateLimit.allowed) {
      const retryAfter = Math.ceil((rateLimit.resetTime - Date.now()) / 1000);
      return createRateLimitResponse(clientIP, retryAfter);
    }

    const supabase = await createClient();
    const { data: { user } } = await supabase.auth.getUser();
    if (!user) {
      return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
    }

    const { session_id: sessionId, match_id: matchId } = await request.json();
    if (!sessionId || !matchId) {
      return NextResponse.json({ error: "Missing session_id or match_id" }, { status: 400 });
    }

    // Verify the Checkout Session is actually paid
    const session = await stripe.checkout.sessions.retrieve(sessionId);
    if (session.payment_status !== "paid") {
      return NextResponse.json({ error: "Payment not completed" }, { status: 400 });
    }

    // Verify the session metadata matches
    if (session.metadata?.match_id !== matchId) {
      return NextResponse.json({ error: "Session does not match" }, { status: 403 });
    }

    const admin = getSupabaseAdmin();

    // Check if already hired (idempotent)
    const { data: match } = await admin
      .from("matches")
      .select("id, status, worker_id, jobs!inner(title, company_id, companies!inner(profile_id))")
      .eq("id", matchId)
      .single();

    if (!match) {
      return NextResponse.json({ error: "Match not found" }, { status: 404 });
    }

    // Verify user owns this match's job
    const jobs = match.jobs as unknown as { title: string; company_id: string; companies: { profile_id: string } };
    if (jobs.companies.profile_id !== user.id) {
      return NextResponse.json({ error: "Not authorized" }, { status: 403 });
    }

    // If already hired, just return success (idempotent)
    if (match.status === "hired") {
      return NextResponse.json({ success: true, alreadyHired: true });
    }

    // Update match to hired
    const hiredAt = new Date().toISOString();
    const piId = typeof session.payment_intent === "string" 
      ? session.payment_intent 
      : session.payment_intent?.id;

    const { error: matchUpdateError } = await admin
      .from("matches")
      .update({
        status: "hired",
        contractor_action: "accepted",
        hired_at: hiredAt,
        fee_charged: true,
      })
      .eq("id", matchId);

    if (matchUpdateError) {
      logger.error("Failed to update match to hired:", matchUpdateError);
      return NextResponse.json({ error: "Failed to update match status" }, { status: HTTP_STATUS_INTERNAL_SERVER_ERROR });
    }

    // Update payment record with real PI id
    if (piId) {
      const { error: paymentUpdateError } = await admin
        .from("payments")
        .update({
          status: "charged",
          stripe_payment_id: piId,
        })
        .eq("match_id", matchId);
      
      if (paymentUpdateError) {
        logger.error("Failed to update payment record:", paymentUpdateError);
        // We do not abort the response because the match is hired, but we log the issue
      }
    }

    // Record success milestones (fire-and-forget)
    try {
      // Check if this is contractor's first or repeat hire
      const { count: hireCount } = await admin
        .from("matches")
        .select("*", { count: "exact", head: true })
        .eq("status", "hired")
        .in("id", [matchId]); // Just count to see context

      const { data: allHires } = await admin
        .from("matches")
        .select("id, hired_at, jobs!inner(company_id, companies!inner(profile_id))")
        .eq("status", "hired");

      const contractorHires = allHires?.filter(
        (h: any) => h.jobs?.companies?.profile_id === user.id
      ) || [];

      if (contractorHires.length <= 1) {
        // First hire for contractor
        await recordMilestone(user.id, "contractor", "first_hire", { match_id: matchId, job_title: jobs.title });
      } else {
        // Repeat hire
        await recordRepeatMilestone(user.id, "contractor", "repeat_hire", { match_id: matchId, hire_number: contractorHires.length });
      }

      // First hire for worker (check if they have other hired matches)
      if (match.worker_id) {
        const { count: workerHireCount } = await admin
          .from("matches")
          .select("*", { count: "exact", head: true })
          .eq("worker_id", match.worker_id)
          .eq("status", "hired");

        if ((workerHireCount || 0) <= 1) {
          await recordMilestone(match.worker_id, "worker", "first_hire", { match_id: matchId, job_title: jobs.title });
        } else {
          await recordRepeatMilestone(match.worker_id, "worker", "repeat_hire", { match_id: matchId });
        }
      }

      // Check for fast hire (within 24h of job posting)
      const { data: jobData } = await admin
        .from("jobs")
        .select("created_at")
        .eq("company_id", jobs.company_id)
        .order("created_at", { ascending: false })
        .limit(1)
        .single();

      if (jobData?.created_at) {
        const jobCreated = new Date(jobData.created_at).getTime();
        const now = Date.now();
        if (now - jobCreated < 24 * 60 * 60 * 1000) {
          await recordMilestone(user.id, "contractor", "fast_hire", { match_id: matchId, hours: Math.round((now - jobCreated) / 3600000) });
        }
      }
    } catch (milestoneErr) {
      // Don't fail the hire if milestone tracking fails
      logger.error("Milestone tracking error on hire:", milestoneErr);
    }

    // Auto-create conversation between contractor and worker
    // Note: Uses direct admin inserts instead of RPC because the RPC function
    // isn't SECURITY DEFINER, so RLS blocks participant inserts from service role
    let conversationId: string | null = null;
    if (user.id && match.worker_id) {
      try {
        // Check if conversation already exists between these two users
        const { data: existingParts } = await admin
          .from("conversation_participants")
          .select("conversation_id")
          .eq("profile_id", user.id);

        if (existingParts && existingParts.length > 0) {
          const convIds = existingParts.map((p: { conversation_id: string }) => p.conversation_id);
          const { data: matchingPart } = await admin
            .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;
          }
        }

        // Create new conversation if none exists
        if (!conversationId) {
          const { data: newConv, error: convCreateErr } = await admin
            .from("conversations")
            .insert({})
            .select("id")
            .single();

          if (convCreateErr || !newConv) {
            logger.error("Failed to create conversation:", convCreateErr);
          } else {
            const { error: partErr } = await admin
              .from("conversation_participants")
              .insert([
                { conversation_id: newConv.id, profile_id: user.id },
                { conversation_id: newConv.id, profile_id: match.worker_id },
              ]);

            if (partErr) {
              logger.error("Failed to add conversation participants:", partErr);
            } else {
              conversationId = newConv.id;
            }
          }
        }
      } catch (convErr) {
        logger.error("Failed to auto-create conversation on hire:", convErr);
      }
    }

    // Notify the worker
    const { error: notifError } = await admin
      .from("notifications")
      .insert({
        profile_id: match.worker_id,
        type: "hire_confirmed",
        title: "You've been hired! 🎉",
        body: `Congratulations! You've been hired for "${jobs.title}". The contractor will be in touch.`,
        data: { matchId, jobTitle: jobs.title, ...(conversationId && { conversationId }) },
      });

    if (notifError) {
      logger.error("Failed to create hire notification:", notifError);
    }

    // Send SMS to worker about hire (fire-and-forget, don't block response)
    try {
      const { data: workerProfile } = await admin
        .from("profiles")
        .select("phone")
        .eq("id", match.worker_id)
        .single();

      if (workerProfile?.phone && isValidAuMobile(workerProfile.phone)) {
        // Get contractor company name
        const { data: company } = await admin
          .from("companies")
          .select("name")
          .eq("profile_id", user.id)
          .single();

        const smsBody = SMS_TEMPLATES.workerHired(
          jobs.title,
          company?.name || "A contractor"
        );
        sendSms(workerProfile.phone, smsBody).catch((err) =>
          logger.error("Hire SMS failed:", err)
        );
      }
    } catch (smsErr) {
      logger.error("SMS notification error on hire:", smsErr);
    }

    // Send SMS to contractor confirming hire (fire-and-forget)
    try {
      const { data: contractorProfile } = await admin
        .from("profiles")
        .select("phone")
        .eq("id", user.id)
        .single();

      const { data: workerProfile2 } = await admin
        .from("profiles")
        .select("name, phone")
        .eq("id", match.worker_id)
        .single();

      if (contractorProfile?.phone && isValidAuMobile(contractorProfile.phone) && workerProfile2) {
        const smsBody = SMS_TEMPLATES.contractorHireConfirmed(
          jobs.title,
          workerProfile2.name || "Worker",
          workerProfile2.phone || "see app"
        );
        sendSms(contractorProfile.phone, smsBody).catch((err) =>
          logger.error("Contractor hire SMS failed:", err)
        );
      }
    } catch (contractorSmsErr) {
      logger.error("Contractor SMS notification error on hire:", contractorSmsErr);
    }

    // Send email notification to worker (fire-and-forget)
    try {
      const { data: workerForEmail } = await admin
        .from("profiles")
        .select("email, name")
        .eq("id", match.worker_id)
        .single();

      if (workerForEmail?.email) {
        const { data: companyForEmail } = await admin
          .from("companies")
          .select("name")
          .eq("profile_id", user.id)
          .single();

        const template = EMAIL_TEMPLATES.workerHired(
          workerForEmail.name || "Worker",
          jobs.title,
          companyForEmail?.name || "A contractor"
        );
        sendEmail(workerForEmail.email, template.subject, template.html).catch((err) =>
          logger.error("Hire email failed:", err)
        );
      }
    } catch (emailErr) {
      logger.error("Email notification error on hire:", emailErr);
    }

    return NextResponse.json({ success: true, conversationId });
  } catch (error) {
    logger.error("Confirm hire error:", error);
    return NextResponse.json({ error: "Failed to confirm hire" }, { status: HTTP_STATUS_INTERNAL_SERVER_ERROR });
  }
}
