<?php

require_once __DIR__ . '/../db.php';
require_once __DIR__ . '/../ActivityLog.php';

/**
 * Authentication Manager - Handles user login, registration, and session management
 */
class AuthManager {
    private static $currentUser = null;
    private static $currentCompany = null;
    private static $presenceEventsTableEnsured = false;
    
    /**
     * Login user with email and password
     */
    public static function login(string $email, string $password): bool {
        $pdo = getPDO();
        
        $stmt = $pdo->prepare("
            SELECT u.*, c.name as company_name, c.username as company_username, c.subdomain, c.plan_id as company_plan_id, c.subscription_status as company_subscription_status
            FROM users u
            LEFT JOIN companies c ON u.company_id = c.id
            WHERE u.email = ? AND u.is_active = 1
        ");
        
        $stmt->execute([$email]);
        $user = $stmt->fetch();
        
        if ($user && password_verify($password, $user['password_hash'])) {
            // Update last login
            $updateStmt = $pdo->prepare("UPDATE users SET last_login_at = NOW() WHERE id = ?");
            $updateStmt->execute([$user['id']]);
            
            // Create session
            $sessionToken = self::generateSessionToken();
            $expiresAt = date('Y-m-d H:i:s', strtotime('+30 days'));
            
            $sessionStmt = $pdo->prepare("
                INSERT INTO user_sessions (user_id, session_token, expires_at, ip_address, user_agent)
                VALUES (?, ?, ?, ?, ?)
            ");
            
            $sessionStmt->execute([
                $user['id'],
                $sessionToken,
                $expiresAt,
                $_SERVER['REMOTE_ADDR'] ?? '',
                $_SERVER['HTTP_USER_AGENT'] ?? ''
            ]);
            
            // Set session cookie
            setcookie('auth_token', $sessionToken, [
                'expires' => strtotime('+30 days'),
                'path' => '/',
                'domain' => '',
                'secure' => false, // Set to true in production with HTTPS
                'httponly' => true,
                'samesite' => 'Lax'
            ]);
            
            [$userWithWorkspace, $company] = self::resolveCompanyContext($user);

            self::$currentUser = $userWithWorkspace;
            self::$currentCompany = $company;

            // Log successful login for this workspace (if available)
            try {
                if (is_array($company) && !empty($company['id'])) {
                    ActivityLog::log(
                        (int) $company['id'],
                        isset($user['id']) ? (int) $user['id'] : null,
                        'user_login',
                        null,
                        null,
                        null,
                        null,
                        null
                    );
                }
            } catch (Throwable $e) {
                // Never break login flow if activity logging fails
            }
            
            return true;
        }
        
        return false;
    }
    
    /**
     * Register new user and company
     */
    public static function register(array $userData, array $companyData): array {
        $pdo = getPDO();
        $pdo->beginTransaction();
        
        try {
            // Create company
            $companyStmt = $pdo->prepare("
                INSERT INTO companies (name, subdomain, plan_id, subscription_status, subscription_expires_at)
                VALUES (?, ?, ?, ?, ?)
            ");
            
            $subdomain = self::generateSubdomain($companyData['name']);
            $trialExpires = date('Y-m-d H:i:s', strtotime('+14 days'));
            
            $companyStmt->execute([
                $companyData['name'],
                $subdomain,
                1, // Starter plan
                'trial',
                $trialExpires
            ]);
            
            $companyId = $pdo->lastInsertId();
            
            // Create user
            $passwordHash = password_hash($userData['password'], PASSWORD_DEFAULT);
            
            $userStmt = $pdo->prepare("
                INSERT INTO users (company_id, name, email, password_hash, role, email_verified_at)
                VALUES (?, ?, ?, ?, ?, NOW())
            ");
            
            $userStmt->execute([
                $companyId,
                $userData['name'],
                $userData['email'],
                $passwordHash,
                'super_admin'
            ]);
            
            $userId = $pdo->lastInsertId();
            
            $pdo->commit();
            
            return [
                'success' => true,
                'company_id' => $companyId,
                'user_id' => $userId,
                'subdomain' => $subdomain
            ];
            
        } catch (Exception $e) {
            $pdo->rollback();
            throw $e;
        }
    }

    /**
     * Ensure there is at least one default company/admin account for recovery.
     * Returns array describing any created entities.
     */
    public static function ensureDefaultAdminAccount(): array
    {
        $pdo = getPDO();
        $result = [
            'company_created' => false,
            'admin_created' => false,
            'access_created' => false,
        ];

        $defaultCompanyName = 'Keelance HQ';
        $defaultCompanyUsername = 'keelance';
        $defaultPlanId = 1;
        $adminEmail = 'admin@keelance.com';
        $adminName = 'Admin User';
        $defaultPassword = 'admin123';

        try {
            $pdo->beginTransaction();

            // Ensure we have at least one company to attach the admin to
            $stmt = $pdo->prepare('SELECT * FROM companies WHERE username = ? ORDER BY id ASC LIMIT 1');
            $stmt->execute([$defaultCompanyUsername]);
            $company = $stmt->fetch();

            if (!$company) {
                $subdomain = substr(preg_replace('/[^a-zA-Z0-9]/', '', strtolower($defaultCompanyUsername)), 0, 20) ?: 'keelance';
                $stmt = $pdo->prepare("INSERT INTO companies (name, username, subdomain, plan_id, subscription_status, created_at, updated_at) VALUES (?, ?, ?, ?, 'active', NOW(), NOW())");
                $stmt->execute([$defaultCompanyName, $defaultCompanyUsername, $subdomain, $defaultPlanId]);
                $companyId = (int) $pdo->lastInsertId();
                $company = [
                    'id' => $companyId,
                    'name' => $defaultCompanyName,
                    'username' => $defaultCompanyUsername,
                ];
                $result['company_created'] = true;
            }

            $companyId = (int) ($company['id'] ?? 0);

            // Ensure admin user exists
            $stmt = $pdo->prepare('SELECT * FROM users WHERE email = ? LIMIT 1');
            $stmt->execute([$adminEmail]);
            $user = $stmt->fetch();

            if (!$user) {
                $passwordHash = password_hash($defaultPassword, PASSWORD_DEFAULT);
                $userStmt = $pdo->prepare("INSERT INTO users (company_id, name, email, password_hash, role, is_active, email_verified_at, created_at, updated_at) VALUES (?, ?, ?, ?, 'super_admin', 1, NOW(), NOW(), NOW())");
                $userStmt->execute([$companyId, $adminName, $adminEmail, $passwordHash]);
                $userId = (int) $pdo->lastInsertId();
                $user = [
                    'id' => $userId,
                    'company_id' => $companyId,
                ];
                $result['admin_created'] = true;
            } else {
                $userId = (int) $user['id'];
                if (empty($user['company_id']) && $companyId) {
                    $update = $pdo->prepare('UPDATE users SET company_id = ? WHERE id = ?');
                    $update->execute([$companyId, $userId]);
                }
            }

            // Ensure mapping table exists before we try to grant access
            $pdo->exec("CREATE TABLE IF NOT EXISTS user_company_access (
                id INT PRIMARY KEY AUTO_INCREMENT,
                user_id INT NOT NULL,
                company_id INT NOT NULL,
                role ENUM('owner','admin','member') DEFAULT 'member',
                created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
                UNIQUE KEY unique_user_company (user_id, company_id)
            )");

            // Ensure admin has access entry for the workspace
            $stmt = $pdo->prepare('SELECT id FROM user_company_access WHERE user_id = ? AND company_id = ?');
            $stmt->execute([$userId, $companyId]);
            if (!$stmt->fetch()) {
                $insert = $pdo->prepare('INSERT INTO user_company_access (user_id, company_id, role, created_at) VALUES (?, ?, ?, NOW())');
                $insert->execute([$userId, $companyId, 'owner']);
                $result['access_created'] = true;
            }

            $pdo->commit();
        } catch (Exception $e) {
            if ($pdo->inTransaction()) {
                $pdo->rollBack();
            }
            throw $e;
        }

        return $result;
    }
    
    /**
     * Logout user
     */
    public static function logout(): void {
        // Capture current user/company context before clearing session
        $existingUser = self::getCurrentUser();
        $logoutCompanyId = null;
        $logoutUserId = null;

        if (is_array($existingUser)) {
            $logoutUserId = isset($existingUser['id']) ? (int) $existingUser['id'] : null;
            if (!empty($existingUser['company_id'])) {
                $logoutCompanyId = (int) $existingUser['company_id'];
            }
        }

        if (isset($_COOKIE['auth_token'])) {
            $sessionToken = $_COOKIE['auth_token'];
            
            // Remove session from database
            $pdo = getPDO();
            $stmt = $pdo->prepare("DELETE FROM user_sessions WHERE session_token = ?");
            $stmt->execute([$sessionToken]);
            
            // Clear cookie
            setcookie('auth_token', '', [
                'expires' => time() - 3600,
                'path' => '/',
                'httponly' => true
            ]);
        }

        // Log logout event after session cleanup (best-effort only)
        if ($logoutCompanyId) {
            try {
                ActivityLog::log(
                    $logoutCompanyId,
                    $logoutUserId,
                    'user_logout',
                    null,
                    null,
                    null,
                    null,
                    null
                );
            } catch (Throwable $e) {
                // Ignore logging failures
            }
        }

        self::$currentUser = null;
        self::$currentCompany = null;
    }
    
    /**
     * Get current authenticated user
     */
    public static function getCurrentUser(): ?array {
        if (self::$currentUser !== null) {
            return self::$currentUser;
        }
        
        if (isset($_COOKIE['auth_token'])) {
            $sessionToken = $_COOKIE['auth_token'];
            
            $pdo = getPDO();
            $stmt = $pdo->prepare("
                SELECT u.*, c.name as company_name, c.username as company_username, c.subdomain, c.plan_id as company_plan_id, c.subscription_status as company_subscription_status
                FROM user_sessions s
                JOIN users u ON s.user_id = u.id
                LEFT JOIN companies c ON u.company_id = c.id
                WHERE s.session_token = ? AND s.expires_at > NOW() AND u.is_active = 1
            ");
            
            $stmt->execute([$sessionToken]);
            $user = $stmt->fetch();
            
            if ($user) {
                [$userWithWorkspace, $company] = self::resolveCompanyContext($user);
                self::$currentUser = $userWithWorkspace;
                self::$currentCompany = $company;
                
                return $userWithWorkspace;
            }
        }
        
        return null;
    }
    
    /**
     * Get current company context
     */
    public static function getCurrentCompany(): ?array {
        if (self::$currentCompany !== null) {
            return self::$currentCompany;
        }
        
        self::getCurrentUser(); // This will set current company
        return self::$currentCompany;
    }
    
    /**
     * Check if user is authenticated
     */
    public static function isAuthenticated(): bool {
        return self::getCurrentUser() !== null;
    }
    
    /**
     * Check if user has specific role
     */
    public static function hasRole(string $role): bool {
        $user = self::getCurrentUser();
        return $user && $user['role'] === $role;
    }
    
    /**
     * Require authentication - redirect to login if not authenticated
     */
    public static function requireAuth(): void {
        if (!self::isAuthenticated()) {
            header('Location: /login.php');
            exit;
        }
    }
    
    /**
     * Generate secure session token
     */
    private static function generateSessionToken(): string {
        return bin2hex(random_bytes(32));
    }

    public static function updateUserPresence(?int $userId = null, ?int $companyId = null, ?string $pageUrl = null): void
    {
        try {
            $user = $userId ? null : self::getCurrentUser();
            if ($userId === null) {
                if (!$user || empty($user['id'])) {
                    return;
                }
                $userId = (int) $user['id'];
            }

            if ($companyId === null) {
                if ($user && !empty($user['company_id'])) {
                    $companyId = (int) $user['company_id'];
                } else {
                    $company = self::getCurrentCompany();
                    if ($company && !empty($company['id'])) {
                        $companyId = (int) $company['id'];
                    }
                }
            }

            $pdo = getPDO();
            $url = $pageUrl ?? ($_SERVER['REQUEST_URI'] ?? null);
            if ($url !== null && strlen($url) > 500) {
                $url = substr($url, 0, 500);
            }

            $sessionId = $_COOKIE['auth_token'] ?? (session_id() ?: null);
            $ip = $_SERVER['REMOTE_ADDR'] ?? null;

            $stmt = $pdo->prepare('
                INSERT INTO user_presence (user_id, company_id, page_url, last_activity_at, session_id, ip_address)
                VALUES (:user_id, :company_id, :page_url, NOW(), :session_id, :ip_address)
                ON DUPLICATE KEY UPDATE
                    company_id = VALUES(company_id),
                    page_url = VALUES(page_url),
                    last_activity_at = VALUES(last_activity_at),
                    session_id = VALUES(session_id),
                    ip_address = VALUES(ip_address)
            ');

            $stmt->execute([
                ':user_id' => $userId,
                ':company_id' => $companyId,
                ':page_url' => $url,
                ':session_id' => $sessionId,
                ':ip_address' => $ip,
            ]);

            self::logPresenceEvent($pdo, $userId, $companyId, $url, $sessionId, $ip);
        } catch (Throwable $e) {
        }
    }

    private static function logPresenceEvent(PDO $pdo, int $userId, ?int $companyId, ?string $url, ?string $sessionId, ?string $ip): void
    {
        self::ensurePresenceEventsTable($pdo);

        $eventStmt = $pdo->prepare('
            INSERT INTO user_presence_events (user_id, company_id, page_url, activity_at, session_id, ip_address)
            VALUES (:user_id, :company_id, :page_url, NOW(), :session_id, :ip_address)
        ');

        $eventStmt->execute([
            ':user_id' => $userId,
            ':company_id' => $companyId,
            ':page_url' => $url,
            ':session_id' => $sessionId,
            ':ip_address' => $ip,
        ]);

        if (mt_rand(1, 500) === 1) {
            $cleanup = $pdo->prepare('DELETE FROM user_presence_events WHERE activity_at < DATE_SUB(NOW(), INTERVAL 30 DAY)');
            $cleanup->execute();
        }
    }

    private static function ensurePresenceEventsTable(PDO $pdo): void
    {
        if (self::$presenceEventsTableEnsured) {
            return;
        }

        $pdo->exec('
            CREATE TABLE IF NOT EXISTS user_presence_events (
                id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
                user_id INT NOT NULL,
                company_id INT NULL,
                page_url VARCHAR(500) NULL,
                activity_at DATETIME NOT NULL,
                session_id VARCHAR(255) NULL,
                ip_address VARCHAR(100) NULL,
                created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
                INDEX idx_presence_activity (activity_at),
                INDEX idx_presence_user (user_id),
                INDEX idx_presence_company (company_id)
            ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
        ');

        self::$presenceEventsTableEnsured = true;
    }

    /**
     * Ensure the authenticated user has up-to-date workspace context.
     * Returns [userWithWorkspaceData, companyArray|null]
     */
    private static function resolveCompanyContext(array $user): array
    {
        $pdo = getPDO();
        $company = null;

        if (!empty($user['company_id'])) {
            $company = self::fetchCompanyById((int) $user['company_id']);
        }

        if (!$company) {
            $company = self::fetchFirstAccessibleCompany((int) $user['id']);
            if ($company) {
                try {
                    $stmt = $pdo->prepare('UPDATE users SET company_id = ? WHERE id = ?');
                    $stmt->execute([$company['id'], $user['id']]);
                    $user['company_id'] = $company['id'];
                } catch (Exception $e) {
                    // If we cannot update, continue with in-memory context
                }
            }
        }

        if ($company) {
            $user['company_id'] = $company['id'];
            $user['company_name'] = $company['name'];
            $user['company_username'] = $company['username'] ?? null;
            $user['subdomain'] = $company['subdomain'] ?? $user['subdomain'] ?? null;
            $user['plan_id'] = $company['plan_id'] ?? $user['plan_id'] ?? null;
            $user['subscription_status'] = $company['subscription_status'] ?? $user['subscription_status'] ?? null;
        } else {
            $user['company_id'] = null;
            $user['company_name'] = $user['company_name'] ?? null;
            $user['company_username'] = $user['company_username'] ?? null;
        }

        return [$user, $company];
    }

    private static function fetchCompanyById(int $companyId): ?array
    {
        if ($companyId <= 0) {
            return null;
        }

        try {
            $pdo = getPDO();
            $stmt = $pdo->prepare('SELECT * FROM companies WHERE id = ?');
            $stmt->execute([$companyId]);
            $company = $stmt->fetch();
            return $company ?: null;
        } catch (Exception $e) {
            return null;
        }
    }

    private static function fetchFirstAccessibleCompany(int $userId): ?array
    {
        if ($userId <= 0) {
            return null;
        }

        try {
            $pdo = getPDO();
            $stmt = $pdo->prepare('
                SELECT c.* FROM user_company_access uca
                JOIN companies c ON c.id = uca.company_id
                WHERE uca.user_id = ?
                ORDER BY c.created_at ASC
                LIMIT 1
            ');
            $stmt->execute([$userId]);
            $company = $stmt->fetch();
            return $company ?: null;
        } catch (Exception $e) {
            return null;
        }
    }
    
    /**
     * Generate unique subdomain from company name
     */
    private static function generateSubdomain(string $companyName): string {
        $base = strtolower(preg_replace('/[^a-zA-Z0-9]/', '', $companyName));
        $subdomain = substr($base, 0, 20);
        
        $pdo = getPDO();
        $counter = 1;
        
        while (true) {
            $stmt = $pdo->prepare("SELECT id FROM companies WHERE subdomain = ?");
            $stmt->execute([$subdomain]);
            
            if ($stmt->rowCount() === 0) {
                break;
            }
            
            $subdomain = substr($base, 0, 18) . $counter;
            $counter++;
        }
        
        return $subdomain;
    }
    
    /**
     * Clean up expired sessions
     */
    public static function cleanupExpiredSessions(): int {
        $pdo = getPDO();
        $stmt = $pdo->prepare("DELETE FROM user_sessions WHERE expires_at < NOW()");
        $stmt->execute();
        
        return $stmt->rowCount();
    }
}
