<?php

namespace App\Http\Controllers;

use App\Models\Audit\TriggerAuditLog;
use App\Services\PartitionManagementService;
use App\Services\TriggerManagementService;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\View\View;

class TriggerAuditController extends Controller
{
    public function __construct(
        private TriggerManagementService $triggerService,
        private PartitionManagementService $partitionService,
    ) {}

    /**
     * 트리거 감사 로그 목록 + 통계 대시보드
     */
    public function index(Request $request): View
    {
        $query = TriggerAuditLog::query()
            ->select(['id', 'table_name', 'row_id', 'dml_type', 'changed_columns', 'operation_id', 'created_at'])
            ->orderByDesc('created_at');

        // 필터링
        if ($request->filled('table_name')) {
            $query->where('table_name', $request->table_name);
        }
        if ($request->filled('dml_type')) {
            $query->where('dml_type', $request->dml_type);
        }
        if ($request->filled('tenant_id')) {
            $query->where('tenant_id', (int) $request->tenant_id);
        }
        if ($request->filled('row_id')) {
            $query->where('row_id', $request->row_id);
        }
        if ($request->filled('from')) {
            $query->where('created_at', '>=', $request->from.' 00:00:00');
        }
        if ($request->filled('to')) {
            $query->where('created_at', '<=', $request->to.' 23:59:59');
        }

        $logs = $query->paginate(50)->withQueryString();

        // 통계
        $stats = $this->getStats();

        // 테이블 목록 (필터용)
        $tables = TriggerAuditLog::selectRaw('table_name, COUNT(*) as cnt')
            ->groupBy('table_name')
            ->orderByDesc('cnt')
            ->pluck('cnt', 'table_name')
            ->toArray();

        // 파티션 현황
        $partitions = DB::select("
            SELECT PARTITION_NAME, PARTITION_DESCRIPTION, TABLE_ROWS
            FROM INFORMATION_SCHEMA.PARTITIONS
            WHERE TABLE_SCHEMA = ? AND TABLE_NAME = 'trigger_audit_logs'
            AND PARTITION_NAME IS NOT NULL
            ORDER BY PARTITION_ORDINAL_POSITION
        ", [config('database.connections.mysql.database')]);

        // 트리거 수
        $triggerCount = DB::selectOne("
            SELECT COUNT(*) as cnt FROM INFORMATION_SCHEMA.TRIGGERS
            WHERE TRIGGER_SCHEMA = ? AND TRIGGER_NAME LIKE 'trg_%'
        ", [config('database.connections.mysql.database')])->cnt;

        return view('trigger-audit.index', compact('logs', 'stats', 'tables', 'partitions', 'triggerCount'));
    }

    /**
     * 트리거 감사 로그 상세 (diff 뷰)
     */
    public function show(int $id): View
    {
        $log = TriggerAuditLog::findOrFail($id);

        // diff 계산
        $diff = $this->calculateDiff($log);

        return view('trigger-audit.show', compact('log', 'diff'));
    }

    /**
     * 특정 레코드의 변경 이력
     */
    public function recordHistory(string $tableName, string $rowId): View
    {
        $logs = TriggerAuditLog::forRecord($tableName, $rowId)
            ->orderByDesc('created_at')
            ->paginate(50);

        return view('trigger-audit.history', compact('logs', 'tableName', 'rowId'));
    }

    /**
     * 롤백 미리보기
     */
    public function rollbackPreview(int $id): View
    {
        $log = TriggerAuditLog::findOrFail($id);
        $sql = $this->generateRollbackSQL($log);

        return view('trigger-audit.rollback-preview', compact('log', 'sql'));
    }

    /**
     * 롤백 실행
     */
    public function rollbackExecute(Request $request, int $id)
    {
        $request->validate(['confirm' => 'required|accepted']);

        $log = TriggerAuditLog::findOrFail($id);
        $sql = $this->generateRollbackSQL($log);

        DB::statement('SET @disable_audit_trigger = 1');

        try {
            DB::transaction(function () use ($sql) {
                DB::statement($sql);
            });

            return redirect()->route('trigger-audit.show', $id)
                ->with('success', '롤백이 성공적으로 실행되었습니다.');
        } catch (\Throwable $e) {
            return redirect()->route('trigger-audit.rollback-preview', $id)
                ->with('error', '롤백 실패: '.$e->getMessage());
        } finally {
            DB::statement('SET @disable_audit_trigger = NULL');
        }
    }

    private function getStats(): array
    {
        $total = TriggerAuditLog::count();

        $byDmlType = TriggerAuditLog::selectRaw('dml_type, COUNT(*) as count')
            ->groupBy('dml_type')
            ->pluck('count', 'dml_type')
            ->toArray();

        $today = TriggerAuditLog::whereDate('created_at', today())->count();

        $topTables = TriggerAuditLog::selectRaw('table_name, COUNT(*) as count')
            ->groupBy('table_name')
            ->orderByDesc('count')
            ->limit(10)
            ->pluck('count', 'table_name')
            ->toArray();

        // 저장소 크기
        $storageInfo = DB::selectOne("
            SELECT
                ROUND(SUM(DATA_LENGTH + INDEX_LENGTH) / 1024 / 1024, 2) as size_mb
            FROM INFORMATION_SCHEMA.TABLES
            WHERE TABLE_SCHEMA = ? AND TABLE_NAME = 'trigger_audit_logs'
        ", [config('database.connections.mysql.database')]);

        return [
            'total' => $total,
            'today' => $today,
            'by_dml_type' => $byDmlType,
            'top_tables' => $topTables,
            'storage_mb' => $storageInfo->size_mb ?? 0,
        ];
    }

    private function calculateDiff(TriggerAuditLog $log): array
    {
        $old = $log->old_values ?? [];
        $new = $log->new_values ?? [];
        $allKeys = array_unique(array_merge(array_keys($old), array_keys($new)));
        sort($allKeys);

        $diff = [];
        foreach ($allKeys as $key) {
            $oldVal = $old[$key] ?? null;
            $newVal = $new[$key] ?? null;
            $changed = $oldVal !== $newVal;

            $diff[] = [
                'column' => $key,
                'old' => $oldVal,
                'new' => $newVal,
                'changed' => $changed,
            ];
        }

        return $diff;
    }

    private function generateRollbackSQL(TriggerAuditLog $log): string
    {
        $pdo = DB::getPdo();

        return match ($log->dml_type) {
            'INSERT' => "DELETE FROM `{$log->table_name}` WHERE `id` = ".$pdo->quote($log->row_id).' LIMIT 1',
            'UPDATE' => $this->buildRevertUpdateSQL($log, $pdo),
            'DELETE' => $this->buildReinsertSQL($log, $pdo),
        };
    }

    private function buildRevertUpdateSQL(TriggerAuditLog $log, \PDO $pdo): string
    {
        if (empty($log->old_values)) {
            return '-- old_values 없음, 롤백 불가';
        }

        $sets = collect($log->old_values)
            ->map(fn ($val, $col) => "`{$col}` = ".($val === null ? 'NULL' : $pdo->quote((string) $val)))
            ->implode(', ');

        return "UPDATE `{$log->table_name}` SET {$sets} WHERE `id` = ".$pdo->quote($log->row_id).' LIMIT 1';
    }

    private function buildReinsertSQL(TriggerAuditLog $log, \PDO $pdo): string
    {
        if (empty($log->old_values)) {
            return '-- old_values 없음, 롤백 불가';
        }

        $cols = collect($log->old_values)->keys()->map(fn ($c) => "`{$c}`")->implode(', ');
        $vals = collect($log->old_values)->values()
            ->map(fn ($v) => $v === null ? 'NULL' : $pdo->quote((string) $v))
            ->implode(', ');

        return "INSERT INTO `{$log->table_name}` ({$cols}) VALUES ({$vals})";
    }

    // ── 4.3 일괄 롤백 (Operation 단위) ──────────────────

    /**
     * Operation 단위 상세 (일괄 롤백 미리보기)
     */
    public function operation(string $operationId): View
    {
        $logs = TriggerAuditLog::forOperation($operationId)
            ->orderBy('created_at')
            ->orderBy('id')
            ->get();

        if ($logs->isEmpty()) {
            abort(404, '해당 작업 ID의 로그가 없습니다.');
        }

        // 각 로그별 롤백 SQL 생성
        $rollbackItems = $logs->reverse()->map(function (TriggerAuditLog $log) {
            return [
                'log' => $log,
                'sql' => $this->generateRollbackSQL($log),
                'diff' => $this->calculateDiff($log),
            ];
        });

        // 요약 정보
        $summary = [
            'operation_id' => $operationId,
            'total_count' => $logs->count(),
            'tables' => $logs->groupBy('table_name')->map->count(),
            'dml_types' => $logs->groupBy('dml_type')->map->count(),
            'actor_id' => $logs->first()->actor_id,
            'session_info' => $logs->first()->session_info,
            'started_at' => $logs->first()->created_at,
            'ended_at' => $logs->last()->created_at,
        ];

        return view('trigger-audit.operation', compact('rollbackItems', 'summary'));
    }

    /**
     * Operation 단위 일괄 롤백 실행
     */
    public function batchRollbackExecute(Request $request, string $operationId)
    {
        $request->validate(['confirm' => 'required|accepted']);

        $logs = TriggerAuditLog::forOperation($operationId)
            ->orderBy('created_at')
            ->orderBy('id')
            ->get();

        if ($logs->isEmpty()) {
            abort(404, '해당 작업 ID의 로그가 없습니다.');
        }

        // 역순으로 롤백 SQL 생성
        $sqls = $logs->reverse()->map(fn (TriggerAuditLog $log) => $this->generateRollbackSQL($log));

        DB::statement('SET @disable_audit_trigger = 1');

        try {
            DB::transaction(function () use ($sqls) {
                foreach ($sqls as $sql) {
                    if (! str_starts_with($sql, '--')) {
                        DB::statement($sql);
                    }
                }
            });

            return redirect()->route('trigger-audit.operation', $operationId)
                ->with('success', "일괄 롤백이 성공적으로 실행되었습니다. ({$logs->count()}건)");
        } catch (\Throwable $e) {
            return redirect()->route('trigger-audit.operation', $operationId)
                ->with('error', '일괄 롤백 실패: '.$e->getMessage());
        } finally {
            DB::statement('SET @disable_audit_trigger = NULL');
        }
    }

    // ── 4.4 트리거 관리 ──────────────────────────────

    /**
     * 트리거 관리 페이지
     */
    public function triggers(): View
    {
        $data = $this->triggerService->getTableTriggerStatus();

        return view('trigger-audit.triggers', compact('data'));
    }

    /**
     * 트리거 재생성 (단일 또는 전체)
     */
    public function regenerateTrigger(Request $request)
    {
        $tableName = $request->input('table_name');

        try {
            $result = $this->triggerService->regenerate($tableName);
        } catch (\Throwable $e) {
            return redirect()->route('trigger-audit.triggers')
                ->with('error', '트리거 재생성 실패: '.$e->getMessage());
        }

        return redirect()->route('trigger-audit.triggers')
            ->with($result['success'] ? 'success' : 'error', $result['message']);
    }

    /**
     * 트리거 삭제 (단일 또는 전체)
     */
    public function dropTrigger(Request $request)
    {
        $tableName = $request->input('table_name');

        try {
            $result = $this->triggerService->drop($tableName);
        } catch (\Throwable $e) {
            return redirect()->route('trigger-audit.triggers')
                ->with('error', '트리거 삭제 실패: '.$e->getMessage());
        }

        return redirect()->route('trigger-audit.triggers')
            ->with($result['success'] ? 'success' : 'error', $result['message']);
    }

    // ── 4.6 파티션 관리 ──────────────────────────────

    /**
     * 파티션 관리 페이지
     */
    public function partitions(): View
    {
        $data = $this->partitionService->getPartitions();

        return view('trigger-audit.partitions', compact('data'));
    }

    /**
     * 미래 파티션 추가
     */
    public function addPartitions(Request $request)
    {
        $request->validate(['months' => 'required|integer|min:1|max:12']);

        try {
            $result = $this->partitionService->addFuturePartitions((int) $request->months);
        } catch (\Throwable $e) {
            return redirect()->route('trigger-audit.partitions')
                ->with('error', '파티션 추가 실패: '.$e->getMessage());
        }

        return redirect()->route('trigger-audit.partitions')
            ->with($result['success'] ? 'success' : 'error', $result['message']);
    }

    /**
     * 파티션 삭제
     */
    public function dropPartition(Request $request)
    {
        $request->validate(['partition_name' => 'required|string']);

        try {
            $result = $this->partitionService->dropPartition($request->partition_name);
        } catch (\Throwable $e) {
            return redirect()->route('trigger-audit.partitions')
                ->with('error', '파티션 삭제 실패: '.$e->getMessage());
        }

        return redirect()->route('trigger-audit.partitions')
            ->with($result['success'] ? 'success' : 'error', $result['message']);
    }
}
