// app/api/push-tokens/save/route.ts
import { NextRequest, NextResponse } from 'next/server';
import pool from '../../../../lib/db/db';
import { ResultSetHeader, RowDataPacket } from 'mysql2';
import { verifyAuthentication } from '../../../utils/verifyAuthentication';

interface TokenInterface {
  id: number;
  userId?: number;
  email?: string;
  role?: string;
}

interface PushTokenRow extends RowDataPacket {
  id: number;
  user_id: number | null;
  expo_push_token: string | null;
  firebase_token: string | null;
  platform: string;
  device_brand: string;
  os_version: string;
  is_active: boolean;
  last_used_at: Date;
  created_at: Date;
  updated_at: Date;
}

// Handle OPTIONS for CORS preflight
export async function OPTIONS() {
  return NextResponse.json({}, { status: 200 });
}

export async function POST(request: NextRequest) {
  const connection = await pool.getConnection();
  
  try {
    // ============ VALIDATION FOR UNAUTHENTICATED ACCESS ============
    const authHeader = request.headers.get('authorization');
    
    // Check if it's an authenticated request (Bearer token) or API_KEY request
    let isAuthenticated = false;
    let authenticatedUserId: number | null = null;
    
    if (authHeader && authHeader.startsWith('Bearer ')) {
      // Try to authenticate as logged-in user
      const token = authHeader.substring(7);
      const authResult = await verifyAuthentication(token);
      
      if (!(authResult instanceof NextResponse)) {
        const decoded = authResult as TokenInterface;
        if (decoded && decoded.id && decoded.role === 'student') {
          authenticatedUserId = decoded.id;
          isAuthenticated = true;
        }
      }
    } else if (authHeader === process.env.APP_API_KEY) {
      // Valid API_KEY for unauthenticated/guest users
      console.log('🔑 Guest user access via API_KEY');
      isAuthenticated = false;
    } else {
      // No valid authentication
      return NextResponse.json({
        success: false,
        message: "Unauthorized Access"
      }, { status: 401 });
    }

    const body = await request.json();
    const {
      expoPushToken,
      firebaseToken,
      userId,
      platform,
      deviceInfo,
    } = body;

    // Validate at least one token is provided
    if (!expoPushToken && !firebaseToken) {
      return NextResponse.json({
        success: false,
        message: "At least one push token is required"
      }, { status: 400 });
    }

    // Determine which user ID to use
    // Priority: authenticated user > provided userId (only if valid API_KEY) > null
    let targetUserId: number | null = null;
    
    if (isAuthenticated && authenticatedUserId) {
      targetUserId = authenticatedUserId;
    } else if (!isAuthenticated && userId) {
      // Only accept userId from body if using API_KEY (guest)
      // But don't trust it completely - just use as is for guest association
      targetUserId = userId;
    }

    await connection.beginTransaction();

    // Check if token already exists
    let existingToken: PushTokenRow | null = null;
    
    if (expoPushToken) {
      const [existing] = await connection.query<PushTokenRow[]>(
        `SELECT * FROM push_tokens 
         WHERE expo_push_token = ? OR firebase_token = ?`,
        [expoPushToken, firebaseToken]
      );
      if (existing.length > 0) {
        existingToken = existing[0];
      }
    } else if (firebaseToken) {
      const [existing] = await connection.query<PushTokenRow[]>(
        `SELECT * FROM push_tokens WHERE firebase_token = ?`,
        [firebaseToken]
      );
      if (existing.length > 0) {
        existingToken = existing[0];
      }
    }

    let result;
    
    if (existingToken) {
      // Update existing token
      const updateFields: any = {};
      if (expoPushToken) updateFields.expo_push_token = expoPushToken;
      if (firebaseToken) updateFields.firebase_token = firebaseToken;
      if (targetUserId !== undefined && targetUserId !== null) updateFields.user_id = targetUserId;
      updateFields.platform = platform || 'unknown';
      updateFields.device_brand = deviceInfo?.brand || 'Unknown';
      updateFields.os_version = deviceInfo?.osVersion?.toString() || 'Unknown';
      updateFields.is_active = true;
      updateFields.last_used_at = new Date();
      updateFields.updated_at = new Date();

      const setClause = Object.keys(updateFields).map(key => `${key} = ?`).join(', ');
      const values = [...Object.values(updateFields), existingToken.id];
      
      const [updateResult] = await connection.query<ResultSetHeader>(
        `UPDATE push_tokens SET ${setClause} WHERE id = ?`,
        values
      );
      
      result = updateResult;
    } else {
      // Insert new token
      const [insertResult] = await connection.query<ResultSetHeader>(
        `INSERT INTO push_tokens (
          user_id, expo_push_token, firebase_token, platform, 
          device_brand, os_version, is_active, last_used_at, created_at, updated_at
        ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
        [
          targetUserId,
          expoPushToken || null,
          firebaseToken || null,
          platform || 'unknown',
          deviceInfo?.brand || 'Unknown',
          deviceInfo?.osVersion?.toString() || 'Unknown',
          true,
          new Date(),
          new Date(),
          new Date()
        ]
      );
      result = insertResult;
    }

    // If user is authenticated, also remove any duplicate tokens for this user
    if (isAuthenticated && authenticatedUserId) {
      await connection.query(
        `DELETE FROM push_tokens 
         WHERE user_id = ? 
         AND id != ? 
         AND (
           (expo_push_token IS NOT NULL AND expo_push_token = ?) OR
           (firebase_token IS NOT NULL AND firebase_token = ?)
         )`,
        [
          authenticatedUserId,
          existingToken?.id || result.insertId,
          expoPushToken,
          firebaseToken
        ]
      );
    }

    await connection.commit();

    return NextResponse.json({
      success: true,
      message: existingToken ? "Push token updated successfully" : "Push token saved successfully",
      data: {
        id: existingToken?.id || result.insertId,
        userId: targetUserId,
        isAuthenticated
      }
    }, { status: 200 });

  } catch (err: any) {
    try {
      await connection.rollback();
    } catch (rollbackErr) {
      console.error("Rollback failed:", rollbackErr);
    }

    console.error("Save push token error:", err);
    return NextResponse.json({
      success: false,
      message: "Internal server error",
      error: err.message
    }, { status: 500 });
  } finally {
    connection.release();
  }
}