<?php
namespace App\Controllers;

use App\Database\Connection;
use App\Repositories\AuditLogRepository;
use App\Services\AuthService;
use App\Utils\Logger;
use App\Utils\Sanitizer;
use Throwable;

class AuditLogController extends ApiController
{
    private AuditLogRepository $repo;
    private AuthService $authService;
    private Connection $db;

    public function __construct(AuditLogRepository $repo, AuthService $authService, Connection $db)
    {
        $this->repo = $repo;
        $this->authService = $authService;
        $this->db = $db;
    }

    public function list()
    {
        if (!$this->authService->isAdmin()) {
            $this->errorResponse('فقط مدیر می‌تواند لاگ‌ها را ببیند');
        }

        $filters = [
            'action' => Sanitizer::sanitize($_POST['action'] ?? ''),
            'entity_type' => Sanitizer::sanitize($_POST['entity_type'] ?? ''),
            'entity_id' => Sanitizer::sanitize($_POST['entity_id'] ?? ''),
            'person_search' => Sanitizer::sanitize($_POST['person_search'] ?? ''),
            'date_from' => Sanitizer::sanitize($_POST['date_from'] ?? ''),
            'date_to' => Sanitizer::sanitize($_POST['date_to'] ?? ''),
            'limit' => (int)($_POST['limit'] ?? 100),
            'offset' => (int)($_POST['offset'] ?? 0),
        ];

        // Normalize empty strings
        foreach (['action','entity_type','entity_id','date_from','date_to','person_search'] as $k) {
            if (isset($filters[$k]) && trim((string)$filters[$k]) === '') {
                $filters[$k] = '';
            }
        }

        $result = $this->repo->list($filters);
        $this->successResponse(['logs' => $result]);
    }

    public function recoverDeletedRecord()
    {
        if (!$this->authService->isAdmin()) {
            $this->errorResponse('فقط مدیر می‌تواند بازیابی انجام دهد');
        }

        $auditLogId = (int)($_POST['audit_log_id'] ?? 0);
        if ($auditLogId <= 0) {
            $this->errorResponse('شناسه لاگ نامعتبر است');
        }

        $log = $this->repo->findById($auditLogId);
        if (!$log) {
            $this->errorResponse('لاگ موردنظر یافت نشد');
        }
        if (($log['action'] ?? '') !== 'delete') {
            $this->errorResponse('فقط رکوردهای حذف‌شده قابل بازیابی هستند');
        }

        $snapshot = json_decode((string)($log['snapshot_json'] ?? ''), true);
        if (!is_array($snapshot)) {
            $this->errorResponse('اسنپ‌شات معتبر برای بازیابی وجود ندارد');
        }

        $entityType = trim((string)($log['entity_type'] ?? ''));
        if ($entityType === 'person') {
            $this->recoverLeadCascade($log, $snapshot);
            return;
        }

        [$table, $row] = $this->resolveRecoveryTarget($entityType, $snapshot);
        if ($table === '' || !is_array($row) || empty($row)) {
            $this->errorResponse('اطلاعات لازم برای بازیابی این رکورد کامل نیست');
        }

        try {
            $insertedId = $this->restoreRow($table, $row);
            $this->successResponse([
                'restored' => true,
                'table' => $table,
                'entity_type' => $entityType,
                'entity_id' => $row['id'] ?? ($log['entity_id'] ?? null),
                'inserted_id' => $insertedId,
            ], 'بازیابی با موفقیت انجام شد');
        } catch (Throwable $e) {
            Logger::logError('Failed to recover deleted record', $e, [
                'audit_log_id' => $auditLogId,
                'entity_type' => $entityType,
                'table' => $table,
            ]);
            $this->errorResponse('خطا در بازیابی: ' . $e->getMessage());
        }
    }

    private function recoverLeadCascade(array $log, array $snapshot): void
    {
        $personRow = is_array($snapshot['person'] ?? null) ? $snapshot['person'] : [];
        if (empty($personRow)) {
            $this->errorResponse('اسنپ‌شات لید ناقص است');
        }

        try {
            $restored = [
                'person' => 0,
                'deals' => 0,
                'activities' => 0,
                'transactions' => 0,
            ];

            $this->restoreRow('persons', $personRow);
            $restored['person'] = 1;

            $deals = is_array($snapshot['deals'] ?? null) ? $snapshot['deals'] : [];
            foreach ($deals as $dealRow) {
                if (!is_array($dealRow) || empty($dealRow)) {
                    continue;
                }
                $this->restoreRow('deals', $dealRow);
                $restored['deals']++;
            }

            $activities = is_array($snapshot['activities'] ?? null) ? $snapshot['activities'] : [];
            foreach ($activities as $activityRow) {
                if (!is_array($activityRow) || empty($activityRow)) {
                    continue;
                }
                $this->restoreRow('activities', $activityRow);
                $restored['activities']++;
            }

            $transactions = is_array($snapshot['transactions'] ?? null) ? $snapshot['transactions'] : [];
            foreach ($transactions as $transactionRow) {
                if (!is_array($transactionRow) || empty($transactionRow)) {
                    continue;
                }
                $this->restoreRow('transactions', $transactionRow);
                $restored['transactions']++;
            }

            $this->successResponse([
                'restored' => true,
                'entity_type' => 'person',
                'entity_id' => $personRow['didar_contact_id'] ?? ($log['entity_id'] ?? null),
                'details' => $restored,
            ], 'بازیابی لید و اطلاعات مرتبط با موفقیت انجام شد');
        } catch (Throwable $e) {
            Logger::logError('Failed to recover lead cascade', $e, [
                'audit_log_id' => $log['id'] ?? null,
                'entity_id' => $log['entity_id'] ?? null,
            ]);
            $this->errorResponse('خطا در بازیابی آبشاری لید: ' . $e->getMessage());
        }
    }

    private function resolveRecoveryTarget(string $entityType, array $snapshot): array
    {
        if ($entityType === 'person') {
            return ['persons', is_array($snapshot['person'] ?? null) ? $snapshot['person'] : []];
        }
        if ($entityType === 'deal') {
            return ['deals', is_array($snapshot['deal'] ?? null) ? $snapshot['deal'] : []];
        }
        if ($entityType === 'activity') {
            return ['activities', is_array($snapshot['activity'] ?? null) ? $snapshot['activity'] : []];
        }
        if ($entityType === 'payment') {
            if (is_array($snapshot['payment'] ?? null)) {
                return ['transactions', $snapshot['payment']];
            }
            if (is_array($snapshot['transaction'] ?? null)) {
                return ['transactions', $snapshot['transaction']];
            }
        }

        return ['', []];
    }

    private function restoreRow(string $table, array $row): int
    {
        $pdo = $this->db->getPdo();
        $columnsStmt = $pdo->query("SHOW COLUMNS FROM `{$table}`");
        $tableColumns = [];
        foreach ($columnsStmt->fetchAll(\PDO::FETCH_ASSOC) as $column) {
            $field = trim((string)($column['Field'] ?? ''));
            if ($field !== '') {
                $tableColumns[$field] = true;
            }
        }

        $payload = [];
        foreach ($row as $key => $value) {
            if (!isset($tableColumns[$key])) {
                continue;
            }
            if (is_array($value) || is_object($value)) {
                $payload[$key] = json_encode($value, JSON_UNESCAPED_UNICODE);
            } else {
                $payload[$key] = $value;
            }
        }

        if (empty($payload)) {
            throw new \RuntimeException('ستون قابل ذخیره‌ای در اسنپ‌شات پیدا نشد');
        }

        $columns = array_keys($payload);
        $quotedColumns = array_map(static function (string $column): string {
            return "`{$column}`";
        }, $columns);
        $placeholders = array_fill(0, count($columns), '?');
        $values = array_map(static function (string $column) use ($payload) {
            return $payload[$column];
        }, $columns);

        $updateColumns = array_values(array_filter($columns, static function (string $column): bool {
            return $column !== 'id';
        }));
        $updateExpr = 'id = id';
        if (!empty($updateColumns)) {
            $updateExpr = implode(', ', array_map(static function (string $column): string {
                return "`{$column}` = VALUES(`{$column}`)";
            }, $updateColumns));
        }

        $sql = "INSERT INTO `{$table}` (" . implode(', ', $quotedColumns) . ")
                VALUES (" . implode(', ', $placeholders) . ")
                ON DUPLICATE KEY UPDATE {$updateExpr}";
        $stmt = $pdo->prepare($sql);
        $stmt->execute($values);

        return (int)$pdo->lastInsertId();
    }
}

