<?php

declare(strict_types=1);

namespace SeoScope\Repositories;

use PDO;

final class UserRepository
{
    public function __construct(
        private PDO $pdo,
        private string $tablePrefix
    ) {
    }

    /**
     * @return array{id:int,email:string,password_hash:string,name:?string,is_active:int,is_owner:int}|null
     */
    public function findByEmail(string $email): ?array
    {
        $email = strtolower(trim($email));
        $table = $this->tablePrefix . 'users';
        $stmt = $this->pdo->prepare("SELECT id, email, password_hash, name, is_active, is_owner FROM `{$table}` WHERE LOWER(email) = ? LIMIT 1");
        $stmt->execute([$email]);
        $row = $stmt->fetch(PDO::FETCH_ASSOC);
        if (!$row) {
            return null;
        }
        return [
            'id' => (int) $row['id'],
            'email' => (string) $row['email'],
            'password_hash' => (string) $row['password_hash'],
            'name' => $row['name'] !== null ? (string) $row['name'] : null,
            'is_active' => isset($row['is_active']) ? (int) $row['is_active'] : 1,
            'is_owner' => isset($row['is_owner']) ? (int) $row['is_owner'] : 0,
        ];
    }

    /**
     * @return array{id:int,email:string,password_hash:string,name:?string,is_active:int,is_owner:int}|null
     */
    public function findById(int $id): ?array
    {
        $id = (int) $id;
        if ($id < 1) {
            return null;
        }
        $table = $this->tablePrefix . 'users';
        $stmt = $this->pdo->prepare("SELECT id, email, password_hash, name, is_active, is_owner FROM `{$table}` WHERE id = ? LIMIT 1");
        $stmt->execute([$id]);
        $row = $stmt->fetch(PDO::FETCH_ASSOC);
        if (!$row) {
            return null;
        }
        return [
            'id' => (int) $row['id'],
            'email' => (string) $row['email'],
            'password_hash' => (string) $row['password_hash'],
            'name' => $row['name'] !== null ? (string) $row['name'] : null,
            'is_active' => isset($row['is_active']) ? (int) $row['is_active'] : 1,
            'is_owner' => isset($row['is_owner']) ? (int) $row['is_owner'] : 0,
        ];
    }

    public function updatePasswordHash(int $userId, string $newPasswordHash): bool
    {
        $userId = (int) $userId;
        if ($userId < 1 || $newPasswordHash === '') {
            return false;
        }
        $table = $this->tablePrefix . 'users';
        $now = date('Y-m-d H:i:s');
        $stmt = $this->pdo->prepare("UPDATE `{$table}` SET password_hash = ?, updated_at = ? WHERE id = ? LIMIT 1");
        $stmt->execute([$newPasswordHash, $now, $userId]);
        return $stmt->rowCount() > 0;
    }

    public function ownerId(): ?int
    {
        $table = $this->tablePrefix . 'users';
        $stmt = $this->pdo->query("SELECT id FROM `{$table}` WHERE is_owner = 1 ORDER BY id ASC LIMIT 1");
        $id = (int) ($stmt ? $stmt->fetchColumn() : 0);
        return $id > 0 ? $id : null;
    }

    /**
     * Creates a password reset token for the given email (if user exists and is active).
     * Returns the raw token (to be emailed) or null.
     */
    public function createPasswordResetToken(string $email, int $ttlSeconds = 3600): ?string
    {
        $email = strtolower(trim($email));
        if ($email === '') {
            return null;
        }
        $table = $this->tablePrefix . 'users';
        $stmt = $this->pdo->prepare("SELECT id, is_active FROM `{$table}` WHERE LOWER(email) = ? LIMIT 1");
        $stmt->execute([$email]);
        $row = $stmt->fetch(PDO::FETCH_ASSOC);
        if (!is_array($row) || (int) ($row['id'] ?? 0) < 1) {
            return null;
        }
        if (isset($row['is_active']) && (int) $row['is_active'] !== 1) {
            return null;
        }

        $token = bin2hex(random_bytes(32));
        $hash = hash('sha256', $token);
        $expiresAt = date('Y-m-d H:i:s', time() + max(60, $ttlSeconds));
        $upd = $this->pdo->prepare("UPDATE `{$table}` SET reset_token_hash = ?, reset_token_expires_at = ? WHERE id = ? LIMIT 1");
        $upd->execute([$hash, $expiresAt, (int) $row['id']]);
        return $token;
    }

    /**
     * @return array{id:int,email:string,name:?string}|null
     */
    public function findByValidResetToken(string $token): ?array
    {
        $token = trim($token);
        if ($token === '') {
            return null;
        }
        $hash = hash('sha256', $token);
        $table = $this->tablePrefix . 'users';
        $now = date('Y-m-d H:i:s');
        $stmt = $this->pdo->prepare(
            "SELECT id, email, name
             FROM `{$table}`
             WHERE reset_token_hash = ? AND reset_token_expires_at IS NOT NULL AND reset_token_expires_at > ?
             LIMIT 1"
        );
        $stmt->execute([$hash, $now]);
        $row = $stmt->fetch(PDO::FETCH_ASSOC);
        if (!is_array($row) || (int) ($row['id'] ?? 0) < 1) {
            return null;
        }
        return [
            'id' => (int) $row['id'],
            'email' => (string) ($row['email'] ?? ''),
            'name' => $row['name'] !== null ? (string) $row['name'] : null,
        ];
    }

    public function consumeResetTokenAndSetPassword(string $token, string $newPasswordHash): bool
    {
        $token = trim($token);
        if ($token === '' || $newPasswordHash === '') {
            return false;
        }
        $hash = hash('sha256', $token);
        $table = $this->tablePrefix . 'users';
        $now = date('Y-m-d H:i:s');
        $stmt = $this->pdo->prepare(
            "UPDATE `{$table}`
             SET password_hash = ?, reset_token_hash = NULL, reset_token_expires_at = NULL, updated_at = ?
             WHERE reset_token_hash = ? AND reset_token_expires_at IS NOT NULL AND reset_token_expires_at > ?
             LIMIT 1"
        );
        $stmt->execute([$newPasswordHash, $now, $hash, $now]);
        return $stmt->rowCount() > 0;
    }

    public function emailExists(string $email, ?int $excludeUserId = null): bool
    {
        $email = strtolower(trim($email));
        if ($email === '') {
            return false;
        }
        $table = $this->tablePrefix . 'users';
        if ($excludeUserId !== null && $excludeUserId > 0) {
            $stmt = $this->pdo->prepare("SELECT 1 FROM `{$table}` WHERE LOWER(email) = ? AND id <> ? LIMIT 1");
            $stmt->execute([$email, $excludeUserId]);
        } else {
            $stmt = $this->pdo->prepare("SELECT 1 FROM `{$table}` WHERE LOWER(email) = ? LIMIT 1");
            $stmt->execute([$email]);
        }
        return (bool) $stmt->fetchColumn();
    }

    public function updateProfile(int $userId, string $email, ?string $name): bool
    {
        $userId = (int) $userId;
        if ($userId < 1) {
            return false;
        }
        $email = strtolower(trim($email));
        $name = $name !== null ? trim($name) : null;
        $name = ($name === '') ? null : $name;

        $table = $this->tablePrefix . 'users';
        $now = date('Y-m-d H:i:s');
        $stmt = $this->pdo->prepare("UPDATE `{$table}` SET email = ?, name = ?, updated_at = ? WHERE id = ? LIMIT 1");
        $stmt->execute([$email, $name, $now, $userId]);
        return $stmt->rowCount() > 0;
    }
}
