<?php
/**
 * admin_generate_pin.php
 * 
 * Admin endpoint to generate withdrawal PINs for users
 * Only admins can generate PINs, users cannot generate their own PINs
 */

header('Content-Type: application/json');
// Critical: Add cache control headers
header('Cache-Control: no-cache, no-store, must-revalidate');
header('Pragma: no-cache');
header('Expires: 0');
// CRITICAL: Allow origin with credentials
$allowed_origin = isset($_SERVER['HTTP_ORIGIN']) ? $_SERVER['HTTP_ORIGIN'] : '*';
header("Access-Control-Allow-Origin: $allowed_origin");
header('Access-Control-Allow-Methods: POST, OPTIONS');
header('Access-Control-Allow-Headers: Content-Type, Authorization');
header('Access-Control-Allow-Credentials: true');
header('X-Content-Type-Options: nosniff');
header('X-Frame-Options: DENY');
header('X-XSS-Protection: 1; mode=block');

// Handle preflight OPTIONS request
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
    http_response_code(200);
    exit;
}

// Define TONSUI_LOADED constant to prevent direct access to session_init.php
define('TONSUI_LOADED', true);

// Include centralized session initialization
require_once __DIR__ . '/session_init.php';

// Require admin authentication
requireAdmin(true);

require_once 'config.php';
require_once 'db.php';

// Validate request method
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
    http_response_code(405);
    echo json_encode(['success' => false, 'message' => 'Method not allowed']);
    exit;
}

// Get and validate POST data
$json_data = file_get_contents('php://input');
$data = json_decode($json_data, true);

if (!$data || !isset($data['user_id'])) {
    http_response_code(400);
    echo json_encode(['success' => false, 'message' => 'Missing required fields']);
    exit;
}

$user_id = intval($data['user_id']);
$manual_pin = isset($data['manual_pin']) ? trim($data['manual_pin']) : null;
$pin_length = isset($data['pin_length']) ? intval($data['pin_length']) : 6;
$expiry_days = array_key_exists('expiry_days', $data)
    ? ($data['expiry_days'] === null ? null : intval($data['expiry_days']))
    : 30;
$notes = isset($data['notes']) ? htmlspecialchars(trim($data['notes']), ENT_QUOTES, 'UTF-8') : null;
$pin_count = isset($data['pin_count']) ? intval($data['pin_count']) : 1;
$pin_name = isset($data['pin_name']) ? htmlspecialchars(trim($data['pin_name']), ENT_QUOTES, 'UTF-8') : null;
$checkpoint_percentage = isset($data['checkpoint_percentage']) ? intval($data['checkpoint_percentage']) : 92;

// Validate checkpoint_percentage (between 1 and 99)
if ($checkpoint_percentage < 1 || $checkpoint_percentage > 99) {
    $checkpoint_percentage = 92; // Default to 92%
}

// Validate manual PIN if provided
if ($manual_pin !== null) {
    if (!preg_match('/^\d{4,10}$/', $manual_pin)) {
        http_response_code(400);
        echo json_encode(['success' => false, 'message' => 'Manual PIN must be 4-10 digits']);
        exit;
    }
    $pin_count = 1; // Manual PIN is always single
} else {
    // Validate pin length (between 4 and 10 digits)
    if ($pin_length < 4 || $pin_length > 10) {
        http_response_code(400);
        echo json_encode(['success' => false, 'message' => 'PIN length must be between 4 and 10 digits']);
        exit;
    }
}

// Validate pin count (between 1 and 10)
if ($pin_count < 1 || $pin_count > 10) {
    http_response_code(400);
    echo json_encode(['success' => false, 'message' => 'PIN count must be between 1 and 10']);
    exit;
}

// Validate expiry days (between 1 and 365, or null for no expiry)
if ($expiry_days !== null && ($expiry_days < 1 || $expiry_days > 365)) {
    http_response_code(400);
    echo json_encode(['success' => false, 'message' => 'Expiry days must be between 1 and 365, or null for no expiry']);
    exit;
}

try {
    // Begin transaction
    $pdo->beginTransaction();
    
    // Check if user exists (schema-tolerant for withdrawal_pin_required)
    $has_wpr = false;
    try {
        $col = $pdo->query("SHOW COLUMNS FROM users LIKE 'withdrawal_pin_required'");
        if ($col && $col->fetch(PDO::FETCH_ASSOC)) { $has_wpr = true; }
    } catch (Exception $e) { /* ignore */ }

    if ($has_wpr) {
        $userStmt = $pdo->prepare("SELECT id, username, email, withdrawal_pin_required FROM users WHERE id = ?");
    } else {
        $userStmt = $pdo->prepare("SELECT id, username, email FROM users WHERE id = ?");
    }
    $userStmt->execute([$user_id]);
    $user = $userStmt->fetch(PDO::FETCH_ASSOC);
    
    if (!$user) {
        throw new Exception('User not found');
    }
    
    // Get user's pin settings (with fallback if table doesn't exist)
    $pin_settings = null;
    try {
        $settingsStmt = $pdo->prepare("
            SELECT * FROM user_pin_settings 
            WHERE user_id = ? 
            LIMIT 1
        ");
        $settingsStmt->execute([$user_id]);
        $pin_settings = $settingsStmt->fetch(PDO::FETCH_ASSOC);
    } catch (PDOException $e) {
        // Table doesn't exist, continue with defaults
        $pin_settings = null;
    }
    
    // Check if withdrawal PIN is enabled for this user
    $pin_enabled = true;
    $max_pins = 5;
    
    if ($pin_settings) {
        $pin_enabled = (bool)$pin_settings['pin_enabled'];
        $max_pins = (int)$pin_settings['max_pins'];
        $default_expiry_days = (int)$pin_settings['pin_expiry_days'];
        
        // Use default expiry from settings if not specified
        if (!isset($data['expiry_days'])) {
            $expiry_days = $default_expiry_days;
        }
    } else {
        // Use setting from user table as fallback if column exists; otherwise default to enabled
        if ($has_wpr && isset($user['withdrawal_pin_required'])) {
            $pin_enabled = (bool)$user['withdrawal_pin_required'];
        } else {
            $pin_enabled = true;
        }
    }
    
    // If withdrawal PIN is not enabled for this user, enable it
    if (!$pin_enabled) {
        // Update or create pin settings (only if table exists)
        try {
            if ($pin_settings) {
                $updateSettingsStmt = $pdo->prepare("
                    UPDATE user_pin_settings 
                    SET pin_enabled = 1, updated_at = NOW() 
                    WHERE user_id = ?
                ");
                $updateSettingsStmt->execute([$user_id]);
            } else {
                $insertSettingsStmt = $pdo->prepare("
                    INSERT INTO user_pin_settings 
                    (user_id, pin_enabled, max_pins, pin_expiry_days, created_at) 
                    VALUES (?, 1, ?, ?, NOW())
                ");
                $insertSettingsStmt->execute([$user_id, $max_pins, $expiry_days]);
            }
        } catch (PDOException $e) {
            // Table doesn't exist, skip settings update
        }
        
        // Update user table for backward compatibility (only if column exists)
        if ($has_wpr) {
            $updateUserStmt = $pdo->prepare("
                UPDATE users 
                SET withdrawal_pin_required = 1
                WHERE id = ?
            ");
            $updateUserStmt->execute([$user_id]);
        }
    }

    // Introspect withdrawal_pins schema
    $has_status = false; $has_used = false; $has_name = false; $has_expiry = false; $has_checkpoint = false; $has_notes = false; $has_created = false;
    try {
        $cols = $pdo->query("SHOW COLUMNS FROM withdrawal_pins");
        while ($c = $cols->fetch(PDO::FETCH_ASSOC)) {
            $f = strtolower($c['Field'] ?? '');
            if ($f === 'status') $has_status = true;
            if ($f === 'used') $has_used = true;
            if ($f === 'name') $has_name = true;
            if ($f === 'expiry_date') $has_expiry = true;
            if ($f === 'checkpoint_percentage') $has_checkpoint = true;
            if ($f === 'notes') $has_notes = true;
            if ($f === 'created_at') $has_created = true;
        }
    } catch (Exception $e) { /* ignore */ }

    // Count active PINs for this user (schema-aware). If expiry_date exists, ignore expired pins.
    $count_mode = 'all';
    $expiry_checked = false;
    if ($has_status) {
        $count_mode = 'status';
        $sql = "SELECT COUNT(*) FROM withdrawal_pins WHERE user_id=? AND status='active'";
        if ($has_expiry) { $sql .= " AND (expiry_date IS NULL OR expiry_date > NOW())"; $expiry_checked = true; }
        $countStmt = $pdo->prepare($sql);
        $countStmt->execute([$user_id]);
    } elseif ($has_used) {
        $count_mode = 'used';
        // In legacy schema, 'used' indicates inactive when 1. No expiry column, so just used=0 or NULL
        $countStmt = $pdo->prepare("SELECT COUNT(*) FROM withdrawal_pins WHERE user_id=? AND (used=0 OR used IS NULL)");
        $countStmt->execute([$user_id]);
    } else {
        // Fallback: count all pins for user
        $countStmt = $pdo->prepare("SELECT COUNT(*) FROM withdrawal_pins WHERE user_id=?");
        $countStmt->execute([$user_id]);
    }
    $active_pin_count = (int)$countStmt->fetchColumn();
    
    // Check if user already has maximum allowed PINs
    if (($active_pin_count + $pin_count) > $max_pins) {
        // Provide clear diagnostic response instead of generic exception
        if ($pdo->inTransaction()) { $pdo->rollBack(); }
        http_response_code(400);
        echo json_encode([
            'success' => false,
            'message' => "User already has {$active_pin_count} active PINs. Maximum allowed is {$max_pins}.",
            'active_pin_count' => $active_pin_count,
            'max_pins' => $max_pins,
            'count_mode' => $count_mode,
            'expiry_checked' => $expiry_checked
        ]);
        exit;
    }
    
    // Generate and store new PINs
    $generated_pins = [];
    
    for ($i = 0; $i < $pin_count; $i++) {
        // Use manual PIN if provided, otherwise generate random
        if ($manual_pin !== null) {
            $pin = $manual_pin;
        } else {
            // Generate a random PIN of specified length
            $min = pow(10, ($pin_length - 1));
            $max = pow(10, $pin_length) - 1;
            $pin = strval(mt_rand($min, $max));
        }
        
        // Calculate expiry date (NULL if no expiry)
        $expiry_date = $expiry_days !== null ? date('Y-m-d H:i:s', strtotime("+{$expiry_days} days")) : null;
        
        // Generate PIN name if not provided
        $default_pin_name = $pin_name ?: "PIN #" . ($i + 1) . " - " . date('M d, Y');
        
        // Idempotency: if an identical active/unused PIN already exists, reuse it instead of inserting a duplicate
        $existing_id = null;
        try {
            if ($has_status) {
                $sql = "SELECT id FROM withdrawal_pins WHERE user_id=? AND pin=? AND status='active'" . ($has_expiry ? " AND (expiry_date IS NULL OR expiry_date > NOW())" : "") . " LIMIT 1";
                $dupStmt = $pdo->prepare($sql);
                $dupStmt->execute([$user_id, $pin]);
            } elseif ($has_used) {
                $dupStmt = $pdo->prepare("SELECT id FROM withdrawal_pins WHERE user_id=? AND pin=? AND (used=0 OR used IS NULL) LIMIT 1");
                $dupStmt->execute([$user_id, $pin]);
            } else {
                $dupStmt = $pdo->prepare("SELECT id FROM withdrawal_pins WHERE user_id=? AND pin=? LIMIT 1");
                $dupStmt->execute([$user_id, $pin]);
            }
            $existing_id = $dupStmt->fetchColumn();
        } catch (Exception $e) { $existing_id = null; }

        if ($existing_id) {
            // Reuse existing record
            $generated_pins[] = [
                'id' => (int)$existing_id,
                'pin' => $pin,
                'name' => $pin_name ?: $default_pin_name,
                'checkpoint_percentage' => $checkpoint_percentage,
                'expiry_date' => $expiry_days === null ? null : date('Y-m-d H:i:s', strtotime("+{$expiry_days} days"))
            ];
            continue;
        }

        // Store the PIN (schema-aware)
        $fields = ['user_id', 'pin'];
        $placeholders = ['?', '?'];
        $values = [$user_id, $pin];

        if ($has_name) { $fields[] = 'name'; $placeholders[] = '?'; $values[] = $default_pin_name; }
        if ($has_checkpoint) { $fields[] = 'checkpoint_percentage'; $placeholders[] = '?'; $values[] = $checkpoint_percentage; }
        if ($has_status) { $fields[] = 'status'; $placeholders[] = "'active'"; }
        elseif ($has_used) { $fields[] = 'used'; $placeholders[] = '0'; }
        if ($has_expiry) { $fields[] = 'expiry_date'; $placeholders[] = '?'; $values[] = $expiry_date; }
        if ($has_notes) { $fields[] = 'notes'; $placeholders[] = '?'; $values[] = $notes; }
        if ($has_created) { $fields[] = 'created_at'; $placeholders[] = 'NOW()'; }

        $sql = 'INSERT INTO withdrawal_pins (' . implode(', ', $fields) . ') VALUES (' . implode(', ', $placeholders) . ')';
        $pinStmt = $pdo->prepare($sql);
        $pinStmt->execute($values);
        $pin_id = $pdo->lastInsertId();
        
        // Add to generated PINs array
        $generated_pins[] = [
            'id' => $pin_id,
            'pin' => $pin,
            'name' => $default_pin_name,
            'checkpoint_percentage' => $checkpoint_percentage,
            'expiry_date' => $expiry_date
        ];
    }
    
    // Log admin action
    $logStmt = $pdo->prepare("
        INSERT INTO admin_logs 
        (admin_id, action, target_type, target_id, details, created_at) 
        VALUES (?, 'generate_pins', 'user', ?, ?, NOW())
    ");
    
    $log_details = json_encode([
        'user_id' => $user_id,
        'pin_count' => $pin_count,
        'pin_length' => $pin_length,
        'expiry_days' => $expiry_days,
        'notes' => $notes
    ]);
    
    $logStmt->execute([isset($_SESSION['admin_id']) ? $_SESSION['admin_id'] : 1, $user_id, $log_details]);
    
    // Commit transaction
    $pdo->commit();
    
    // Return success response
    echo json_encode([
        'success' => true,
        'message' => "Successfully generated {$pin_count} withdrawal " . ($pin_count > 1 ? 'PINs' : 'PIN') . " for user",
        'user' => [
            'id' => $user['id'],
            'username' => $user['username'],
            'email' => $user['email']
        ],
        'pins' => $generated_pins,
        'expiry_days' => $expiry_days,
        'expiry_date' => $expiry_days === null ? null : date('Y-m-d H:i:s', strtotime("+{$expiry_days} days"))
    ]);
    
} catch (PDOException $e) {
    // Rollback transaction if in progress
    if ($pdo->inTransaction()) {
        $pdo->rollBack();
    }
    
    error_log('Database error generating withdrawal PINs: ' . $e->getMessage());
    
    http_response_code(500);
    echo json_encode([
        'success' => false, 
        'message' => 'Database error generating withdrawal PINs',
        'debug' => $e->getMessage() // Temporary debug info
    ]);
    
} catch (Exception $e) {
    // Rollback transaction if in progress
    if ($pdo->inTransaction()) {
        $pdo->rollBack();
    }
    
    http_response_code(400);
    echo json_encode([
        'success' => false, 
        'message' => $e->getMessage()
    ]);
}
