<?php

namespace MintyPHP\Service\Audit;

use MintyPHP\Domain\Taxonomy\UserLifecycleAction;
use MintyPHP\Domain\Taxonomy\UserLifecycleStatus;
use MintyPHP\Domain\Taxonomy\UserLifecycleTriggerType;
use MintyPHP\Repository\Audit\UserLifecycleAuditRepositoryInterface;
use MintyPHP\Support\Crypto;

class UserLifecycleAuditService
{
    private const RETENTION_DAYS = 365;
    private const SNAPSHOT_VERSION = 1;

    private const SNAPSHOT_FIELDS = [
        'uuid',
        'first_name',
        'last_name',
        'display_name',
        'email',
        'profile_description',
        'job_title',
        'phone',
        'mobile',
        'short_dial',
        'address',
        'postal_code',
        'city',
        'region',
        'country',
        'hire_date',
        'locale',
        'theme',
        'email_verified_at',
        'last_login_at',
        'last_login_provider',
        'primary_tenant_id',
        'current_tenant_id',
        'created',
        'modified',
        'active_changed_at',
    ];

    public function __construct(private readonly UserLifecycleAuditRepositoryInterface $userLifecycleAuditRepository)
    {
    }

    public function logDeactivate(
        string $runUuid,
        string $triggerType,
        array $policy,
        ?int $actorUserId,
        array $targetUser,
        string $status = UserLifecycleStatus::Success->value,
        ?string $reasonCode = null
    ): bool {
        try {
            return $this->userLifecycleAuditRepository->create($this->buildBaseRow(
                $runUuid,
                UserLifecycleAction::Deactivate->value,
                $triggerType,
                $status,
                $reasonCode,
                $policy,
                $actorUserId,
                $targetUser
            )) !== false;
        } catch (\Throwable $exception) {
            return false;
        }
    }

    public function logDeleteWithSnapshot(
        string $runUuid,
        string $triggerType,
        array $policy,
        ?int $actorUserId,
        array $targetUser
    ): int|false {
        try {
            $snapshot = $this->buildSnapshot($targetUser);
            $json = json_encode($snapshot, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
            if (!is_string($json)) {
                return false;
            }
            $row = $this->buildBaseRow(
                $runUuid,
                UserLifecycleAction::Delete->value,
                $triggerType,
                UserLifecycleStatus::Success->value,
                null,
                $policy,
                $actorUserId,
                $targetUser
            );
            $row['snapshot_enc'] = Crypto::encryptString($json);
            $row['snapshot_version'] = self::SNAPSHOT_VERSION;
            return $this->userLifecycleAuditRepository->create($row);
        } catch (\Throwable $exception) {
            return false;
        }
    }

    public function logDeleteFailure(
        string $runUuid,
        string $triggerType,
        array $policy,
        ?int $actorUserId,
        array $targetUser,
        string $reasonCode
    ): bool {
        try {
            return $this->userLifecycleAuditRepository->create($this->buildBaseRow(
                $runUuid,
                UserLifecycleAction::Delete->value,
                $triggerType,
                UserLifecycleStatus::Failed->value,
                $reasonCode,
                $policy,
                $actorUserId,
                $targetUser
            )) !== false;
        } catch (\Throwable $exception) {
            return false;
        }
    }

    public function logRestore(
        string $runUuid,
        string $triggerType,
        array $policy,
        ?int $actorUserId,
        array $targetUser,
        string $status = UserLifecycleStatus::Success->value,
        ?string $reasonCode = null
    ): bool {
        try {
            return $this->userLifecycleAuditRepository->create($this->buildBaseRow(
                $runUuid,
                UserLifecycleAction::Restore->value,
                $triggerType,
                $status,
                $reasonCode,
                $policy,
                $actorUserId,
                $targetUser
            )) !== false;
        } catch (\Throwable $exception) {
            return false;
        }
    }

    public function markDeleteEventRestored(int $auditId, int $restoredByUserId, int $restoredUserId): bool
    {
        try {
            return $this->userLifecycleAuditRepository->markRestored($auditId, $restoredByUserId, $restoredUserId);
        } catch (\Throwable $exception) {
            return false;
        }
    }

    public function listPaged(array $filters): array
    {
        return $this->userLifecycleAuditRepository->listPaged($filters);
    }

    public function find(int $id): ?array
    {
        return $this->userLifecycleAuditRepository->find($id);
    }

    public function filterOptions(int $limit = 200): array
    {
        return $this->userLifecycleAuditRepository->listFilterOptions($limit);
    }

    public function findDeleteEventForRestore(int $id, bool $forUpdate = false): ?array
    {
        return $this->userLifecycleAuditRepository->findDeleteEventForRestore($id, $forUpdate);
    }

    public function decryptSnapshot(array $event): ?array
    {
        $enc = trim((string) ($event['snapshot_enc'] ?? ''));
        if ($enc === '') {
            return null;
        }
        try {
            $json = Crypto::decryptString($enc);
            $decoded = json_decode($json, true);
            return is_array($decoded) ? $decoded : null;
        } catch (\Throwable $exception) {
            return null;
        }
    }

    public function updateStatus(int $id, string $status, ?string $reasonCode = null): bool
    {
        try {
            return $this->userLifecycleAuditRepository->updateStatus($id, $status, $reasonCode);
        } catch (\Throwable $exception) {
            return false;
        }
    }

    public function purgeExpired(): int
    {
        return $this->userLifecycleAuditRepository->purgeOlderThanDays(self::RETENTION_DAYS);
    }

    private function buildBaseRow(
        string $runUuid,
        string $action,
        string $triggerType,
        string $status,
        ?string $reasonCode,
        array $policy,
        ?int $actorUserId,
        array $targetUser
    ): array {
        $action = UserLifecycleAction::normalizeOr($action, UserLifecycleAction::Deactivate)->value;
        $triggerType = UserLifecycleTriggerType::normalizeOr($triggerType, UserLifecycleTriggerType::System)->value;
        $status = UserLifecycleStatus::normalizeOr($status, UserLifecycleStatus::Failed)->value;

        return [
            'run_uuid' => trim($runUuid),
            'action' => $action,
            'trigger_type' => $triggerType,
            'status' => $status,
            'reason_code' => $reasonCode !== null ? trim($reasonCode) : null,
            'policy_deactivate_days' => max(0, (int) ($policy['deactivate_days'] ?? 0)),
            'policy_delete_days' => max(0, (int) ($policy['delete_days'] ?? 0)),
            'actor_user_id' => ($actorUserId ?? 0) > 0 ? (int) $actorUserId : null,
            'target_user_id' => ((int) ($targetUser['id'] ?? 0)) > 0 ? (int) $targetUser['id'] : null,
            'target_user_uuid' => $this->stringOrNull($targetUser['uuid'] ?? null),
            'target_user_email' => $this->stringOrNull($targetUser['email'] ?? null),
            'snapshot_enc' => null,
            'snapshot_version' => self::SNAPSHOT_VERSION,
        ];
    }

    private function buildSnapshot(array $targetUser): array
    {
        $snapshot = [];
        foreach (self::SNAPSHOT_FIELDS as $field) {
            $snapshot[$field] = $targetUser[$field] ?? null;
        }
        return $snapshot;
    }

    private function stringOrNull(mixed $value): ?string
    {
        $value = trim((string) $value);
        return $value !== '' ? $value : null;
    }
}
