<?php
/**
 * ShareLink — model for student-access share links.
 *
 * Share links enable students to submit TFEs via unique URLs without
 * requiring admin authentication. Each link has a unique slug, optional
 * password, activity flag, optional expiration, and usage count.
 */
class ShareLink
{
    private Database $db;

    public function __construct(Database $db)
    {
        $this->db = $db;
    }

    public static function make(): self
    {
        require_once APP_ROOT . '/src/Database.php';
        return new self(new Database());
    }

    // ── Slug generation ───────────────────────────────────────────────────────

    /**
     * Generate a unique slug in the format YYYYMMDD-<random>.
     * The random portion uses 8 base32 chars (~40 bits of entropy).
     */
    public static function generateSlug(): string
    {
        $date = date('Ymd');
        $chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567';
        $random = '';
        for ($i = 0; $i < 8; $i++) {
            $random .= $chars[random_int(0, strlen($chars) - 1)];
        }
        return $date . '-' . $random;
    }

    // ── CRUD ──────────────────────────────────────────────────────────────────

    /**
     * Create a new share link.
     *
     * @param int        $createdBy    Admin user ID
     * @param string|null $password    Plain-text password (will be hashed), null = no password
     * @param string|null $expiresAt   ISO-8601 expiration date, null = never expires
     * @return array                   The created link row
     */
    public function create(int $createdBy, ?string $password = null, ?string $expiresAt = null): array
    {
        $slug = self::generateSlug();
        $passwordHash = $password !== null ? password_hash($password, PASSWORD_BCRYPT) : null;

        $stmt = $this->db->getConnection()->prepare(
            "INSERT INTO share_links (slug, password_hash, is_active, created_by, expires_at)
             VALUES (?, ?, 1, ?, ?)"
        );
        $stmt->execute([$slug, $passwordHash, $createdBy, $expiresAt]);

        return $this->findBySlug($slug);
    }

    /**
     * Find a share link by its slug.
     *
     * @return array|null
     */
    public function findBySlug(string $slug): ?array
    {
        $stmt = $this->db->getConnection()->prepare(
            "SELECT * FROM share_links WHERE slug = ?"
        );
        $stmt->execute([$slug]);
        $row = $stmt->fetch();
        return $row ?: null;
    }

    /**
     * Find a share link by its ID.
     *
     * @return array|null
     */
    public function findById(int $id): ?array
    {
        $stmt = $this->db->getConnection()->prepare(
            "SELECT * FROM share_links WHERE id = ?"
        );
        $stmt->execute([$id]);
        $row = $stmt->fetch();
        return $row ?: null;
    }

    /**
     * List all share links, ordered by creation date descending.
     *
     * @return array
     */
    public function listAll(): array
    {
        $stmt = $this->db->getConnection()->query(
            "SELECT * FROM share_links ORDER BY created_at DESC"
        );
        return $stmt->fetchAll();
    }

    /**
     * Toggle the active status of a share link.
     */
    public function toggleActive(int $id): void
    {
        $this->db->getConnection()->prepare(
            "UPDATE share_links SET is_active = NOT is_active WHERE id = ?"
        )->execute([$id]);
    }

    /**
     * Set or clear the password for a share link.
     *
     * @param string|null $password  Plain-text password, or null to clear
     */
    public function setPassword(int $id, ?string $password): void
    {
        $hash = $password !== null ? password_hash($password, PASSWORD_BCRYPT) : null;
        $this->db->getConnection()->prepare(
            "UPDATE share_links SET password_hash = ? WHERE id = ?"
        )->execute([$hash, $id]);
    }

    /**
     * Delete a share link.
     */
    public function delete(int $id): void
    {
        $this->db->getConnection()->prepare(
            "DELETE FROM share_links WHERE id = ?"
        )->execute([$id]);
    }

    /**
     * Increment the usage count for a share link.
     */
    public function incrementUsage(int $id): void
    {
        $this->db->getConnection()->prepare(
            "UPDATE share_links SET usage_count = usage_count + 1 WHERE id = ?"
        )->execute([$id]);
    }

    // ── Validation ────────────────────────────────────────────────────────────

    /**
     * Validate whether a share link is usable.
     *
     * Returns an array:
     *   ['valid' => true]  if the link is active and not expired
     *   ['valid' => false, 'reason' => 'disabled']    if deactivated
     *   ['valid' => false, 'reason' => 'expired']      if past expiration
     *   ['valid' => false, 'reason' => 'not_found']    if slug doesn't exist
     *   ['valid' => false, 'reason' => 'needs_password', 'link' => array]  if password required
     */
    public function validateLink(?string $slug): array
    {
        if ($slug === null || $slug === '') {
            return ['valid' => false, 'reason' => 'not_found'];
        }

        $link = $this->findBySlug($slug);
        if ($link === null) {
            return ['valid' => false, 'reason' => 'not_found'];
        }

        if (!$link['is_active']) {
            return ['valid' => false, 'reason' => 'disabled', 'link' => $link];
        }

        if ($link['expires_at'] !== null && strtotime($link['expires_at']) < time()) {
            return ['valid' => false, 'reason' => 'expired', 'link' => $link];
        }

        if ($link['password_hash'] !== null) {
            return ['valid' => false, 'reason' => 'needs_password', 'link' => $link];
        }

        return ['valid' => true, 'link' => $link];
    }

    /**
     * Verify the password against a share link.
     *
     * @return bool
     */
    public function verifyPassword(array $link, string $password): bool
    {
        if ($link['password_hash'] === null) {
            return true; // No password set
        }
        return password_verify($password, $link['password_hash']);
    }
}
