<?php
namespace EasyChat\Library\Cms;

use EasyChat\Library\Messenger\SecretBox;

final class CmsCustomerIdentityRepository
{
    private $db; private $prefix; private $secret;

    public function __construct($db, string $prefix, string $secret)
    {
        $this->db = $db; $this->prefix = $prefix; $this->secret = trim($secret);
    }

    public function identityHash(string $provider, string $scope, string $subject): string
    {
        return hash_hmac('sha256', strtolower($provider) . "\n" . $scope . "\n" . $subject, $this->secret);
    }

    public function find(string $provider, string $scope, string $subject, bool $for_update = false): array
    {
        if ($this->secret === '') return array();
        $hash = $this->identityHash($provider, $scope, $subject);
        $query = $this->db->query(
            "SELECT * FROM `{$this->prefix}cms_customer_identities`
             WHERE `provider`='" . $this->db->escape($provider) . "'
               AND `scope_hash`='" . $this->db->escape($this->hash($scope)) . "'
               AND `subject_hash`='" . $this->db->escape($hash) . "'
             LIMIT 1" . ($for_update ? ' FOR UPDATE' : '')
        );
        $row = (array)($query->row ?? array());
        if ($row) $row['subject'] = SecretBox::decrypt((string)$row['subject_ciphertext'], $this->secret);
        return $row;
    }

    public function save(int $customer_id, string $provider, string $scope, string $subject, string $now): array
    {
        if ($customer_id <= 0 || $this->secret === '') return array();
        $subject_hash = $this->identityHash($provider, $scope, $subject);
        $ciphertext = SecretBox::encrypt($subject, $this->secret);
        $this->db->query(
            "INSERT INTO `{$this->prefix}cms_customer_identities` SET
                `customer_id`=" . (int)$customer_id . ",
                `provider`='" . $this->db->escape($provider) . "',
                `scope_hash`='" . $this->db->escape($this->hash($scope)) . "',
                `subject_hash`='" . $this->db->escape($subject_hash) . "',
                `subject_ciphertext`='" . $this->db->escape($ciphertext) . "',
                `assurance`='cms_session', `last_seen_at`='" . $this->db->escape($now) . "',
                `date_added`='" . $this->db->escape($now) . "', `date_modified`='" . $this->db->escape($now) . "'
             ON DUPLICATE KEY UPDATE
                `last_seen_at`=VALUES(`last_seen_at`), `date_modified`=VALUES(`date_modified`)"
        );
        return $this->find($provider, $scope, $subject, false);
    }

    public function forCustomer(int $customer_id, string $provider = '', string $scope = ''): array
    {
        if ($customer_id <= 0 || $this->secret === '') return array();
        $where = $provider !== '' ? " AND `provider`='" . $this->db->escape($provider) . "'" : '';
        if ($scope !== '') $where .= " AND `scope_hash`='" . $this->db->escape($this->hash($scope)) . "'";
        $query = $this->db->query("SELECT * FROM `{$this->prefix}cms_customer_identities` WHERE `customer_id`=" . (int)$customer_id . $where . " ORDER BY `last_seen_at` DESC LIMIT 1");
        $row = (array)($query->row ?? array());
        if ($row) $row['subject'] = SecretBox::decrypt((string)$row['subject_ciphertext'], $this->secret);
        return $row;
    }

    public function reassignCustomer(int $identity_id, int $customer_id, string $now): void
    {
        if ($identity_id <= 0 || $customer_id <= 0) return;
        $this->db->query("UPDATE `{$this->prefix}cms_customer_identities` SET `customer_id`=" . (int)$customer_id . ", `date_modified`='" . $this->db->escape($now) . "' WHERE `identity_id`=" . (int)$identity_id);
    }

    private function hash(string $value): string
    {
        return hash_hmac('sha256', $value, $this->secret);
    }
}
