<?php
declare(strict_types=1);

class Auth {
    private DB $db;

    public function __construct(?DB $db = null) {
        $this->db = $db ?? iul_db();
    }

    /** @return array{id:string,email:string,org_id:string,role:string}|null */
    public function userFromToken(?string $token): ?array {
        if (!$token) {
            return null;
        }
        $hash = hash('sha256', $token);
        $row = $this->db->query(
            'SELECT u.id, u.email, p.org_id, p.role
               FROM user_sessions s
               JOIN users u ON u.id = s.user_id
               JOIN profiles p ON p.id = u.id
              WHERE s.token_hash = ?
                AND s.expires_at > CURRENT_TIMESTAMP(3)
                AND u.is_active = 1',
            $hash
        )->fetchArray();
        return $row ?: null;
    }

    /** @return array{token:string,user:array}|null */
    public function login(string $email, string $password): ?array {
        $user = $this->db->query(
            'SELECT id, email, password_hash, full_name FROM users WHERE email = ? AND is_active = 1',
            $email
        )->fetchArray();
        if (!$user || empty($user['password_hash'])) {
            return null;
        }
        if (!password_verify($password, $user['password_hash'])) {
            return null;
        }

        return $this->issueSession($user['id']);
    }

    /**
     * PropHog SSO (Agent Recruiting + Pledge): each user gets their own IUL org so
     * uploads stay private. No IUL password. Token is HMAC-signed server-side.
     *
     * Recruiting tokens carry numeric uid → org slug ar-{uid}.
     * Tracker tokens carry slug {product}-{agent_uuid} (pledge-, mfit-, bigwin-, …; no uid required).
     *
     * @return array{token:string,user:array}|null
     */
    public function loginFromRecruitingSso(string $token): ?array {
        $claims = iul_sso_verify_token($token);
        if (!$claims) {
            return null;
        }
        $slugClaim = trim((string) ($claims['slug'] ?? ''));
        $arUserId = (int) ($claims['uid'] ?? 0);
        $isTracker = $slugClaim !== '' && preg_match('/^(pledge|mfit|bigwin)-[0-9a-f-]{36}$/i', $slugClaim);
        if (!$isTracker && $arUserId <= 0) {
            return null;
        }
        $email = strtolower(trim((string) ($claims['email'] ?? '')));
        $name = trim((string) ($claims['name'] ?? ''));
        $username = trim((string) ($claims['username'] ?? $claims['user'] ?? ''));
        $role = ((int) ($claims['ut'] ?? 0) === 1) ? 'admin' : 'agent';
        if ($isTracker) {
            $slug = strtolower($slugClaim);
            $syntheticEmail = $slug . '@sso.iul.prophog2.com';
            if ($name === '') {
                $name = $email !== '' ? $email : ('Tracker agent ' . substr($slug, strpos($slug, '-') + 1, 8));
            }
            if ($username === '') {
                $username = substr($slug, 0, strpos($slug, '-') ?: strlen($slug));
            }
        } else {
            if ($name === '') {
                $name = $email !== '' ? $email : ('PropHog user ' . $arUserId);
            }
            $slug = 'ar-' . $arUserId;
            $syntheticEmail = 'ar' . $arUserId . '@sso.iul.prophog2.com';
        }
        $loginEmail = (filter_var($email, FILTER_VALIDATE_EMAIL) && !str_ends_with($email, '@sso.iul.prophog2.com'))
            ? $email
            : $syntheticEmail;

        $this->db->beginTransaction();
        try {
            $org = $this->db->query(
                'SELECT id FROM organizations WHERE slug = ? LIMIT 1',
                $slug
            )->fetchArray();

            if (!empty($org['id'])) {
                $orgId = $org['id'];
                $profile = $this->db->query(
                    'SELECT id FROM profiles WHERE org_id = ? ORDER BY created_at ASC LIMIT 1',
                    $orgId
                )->fetchArray();
                if (empty($profile['id'])) {
                    throw new RuntimeException('sso profile missing');
                }
                $userId = $profile['id'];
                $taken = $this->db->query(
                    'SELECT id FROM users WHERE email = ? AND id != ? LIMIT 1',
                    $loginEmail,
                    $userId
                )->fetchArray();
                if (!empty($taken['id'])) {
                    $loginEmail = $syntheticEmail;
                }
                $this->db->query(
                    'UPDATE users SET email = ?, full_name = ?, username = COALESCE(NULLIF(?, \'\'), username), is_active = 1 WHERE id = ?',
                    $loginEmail,
                    $name,
                    $username,
                    $userId
                );
                $this->db->query(
                    'UPDATE organizations SET name = ?, is_active = 1 WHERE id = ?',
                    $name,
                    $orgId
                );
                $this->db->query(
                    'UPDATE profiles SET role = ? WHERE id = ?',
                    $role,
                    $userId
                );
            } else {
                $taken = $this->db->query(
                    'SELECT id FROM users WHERE email = ? LIMIT 1',
                    $loginEmail
                )->fetchArray();
                if (!empty($taken['id'])) {
                    $loginEmail = $syntheticEmail;
                }
                $orgId = iul_uuid();
                $userId = iul_uuid();
                $this->db->query(
                    'INSERT INTO organizations (id, name, slug, is_active) VALUES (?, ?, ?, 1)',
                    $orgId,
                    $name,
                    $slug
                );
                $this->db->query(
                    'INSERT INTO users (id, email, password_hash, full_name, username, is_active) VALUES (?, ?, ?, ?, ?, 1)',
                    $userId,
                    $loginEmail,
                    '',
                    $name,
                    $username !== '' ? $username : null
                );
                $this->db->query(
                    'INSERT INTO profiles (id, org_id, role) VALUES (?, ?, ?)',
                    $userId,
                    $orgId,
                    $role
                );
            }
            $this->db->commit();
        } catch (Throwable $e) {
            $this->db->rollback();
            error_log('IUL SSO provision failed: ' . $e->getMessage());
            return null;
        }

        return $this->issueSession($userId);
    }

    /** @return array{token:string,user:array}|null */
    private function issueSession(string $userId): ?array {
        $user = $this->db->query(
            'SELECT id, email, full_name, username FROM users WHERE id = ? AND is_active = 1',
            $userId
        )->fetchArray();
        if (empty($user['id'])) {
            return null;
        }

        $token = bin2hex(random_bytes(32));
        $hash = hash('sha256', $token);
        $sessionId = iul_uuid();
        $expires = date('Y-m-d H:i:s.v', time() + (int) IUL_SESSION_TTL);
        $ip = $_SERVER['REMOTE_ADDR'] ?? null;
        $ua = substr($_SERVER['HTTP_USER_AGENT'] ?? '', 0, 512);

        $this->db->query(
            'INSERT INTO user_sessions (id, user_id, token_hash, ip_address, user_agent, expires_at)
             VALUES (?, ?, ?, ?, ?, ?)',
            $sessionId,
            $user['id'],
            $hash,
            $ip,
            $ua,
            $expires
        );

        $this->db->query(
            'UPDATE users SET last_login_at = CURRENT_TIMESTAMP(3) WHERE id = ?',
            $user['id']
        );

        $profile = $this->db->query(
            'SELECT org_id, role FROM profiles WHERE id = ?',
            $user['id']
        )->fetchArray();

        if (!empty($profile['org_id'])) {
            try {
                (new IulDemoSeeder($this->db))->ensureForOrg(
                    (string) $profile['org_id'],
                    (string) $user['id']
                );
            } catch (Throwable $e) {
                error_log('IUL demo seed on login: ' . $e->getMessage());
            }
        }

        return [
            'token' => $token,
            'user' => [
                'id' => $user['id'],
                'email' => $user['email'],
                'full_name' => $user['full_name'],
                'username' => $user['username'] ?? null,
                'org_id' => $profile['org_id'] ?? null,
                'role' => $profile['role'] ?? 'agent',
            ],
        ];
    }

    public function requireUser(): array {
        $user = $this->userFromToken(iul_bearer_token());
        if (!$user) {
            iul_json_error('unauthorized', 401);
        }
        return $user;
    }
}
