<?php

namespace App\Services;

use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\DB;

class PartitionManagementService
{
    /**
     * 파티션 현황 조회
     */
    public function getPartitions(): array
    {
        $dbName = config('database.connections.mysql.database');
        $retentionDays = $this->getRetentionDays();

        $partitions = DB::select("
            SELECT PARTITION_NAME, PARTITION_DESCRIPTION, TABLE_ROWS, DATA_LENGTH, INDEX_LENGTH
            FROM INFORMATION_SCHEMA.PARTITIONS
            WHERE TABLE_SCHEMA = ? AND TABLE_NAME = 'trigger_audit_logs'
            AND PARTITION_NAME IS NOT NULL
            ORDER BY PARTITION_ORDINAL_POSITION
        ", [$dbName]);

        // 저장소 전체 크기
        $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'
        ", [$dbName]);

        $totalRows = 0;
        $cutoffTs = Carbon::now()->subDays($retentionDays)->timestamp;
        $nowTs = Carbon::now()->timestamp;

        $items = [];
        foreach ($partitions as $p) {
            $totalRows += $p->TABLE_ROWS;

            if ($p->PARTITION_DESCRIPTION === 'MAXVALUE') {
                $items[] = [
                    'name' => $p->PARTITION_NAME,
                    'bound_raw' => 'MAXVALUE',
                    'bound_date' => null,
                    'rows' => $p->TABLE_ROWS,
                    'status' => 'future',
                    'can_drop' => false,
                ];

                continue;
            }

            $boundTs = (int) $p->PARTITION_DESCRIPTION;
            $boundDate = Carbon::createFromTimestamp($boundTs)->format('Y-m-d');

            // 상태 결정
            if ($boundTs <= $cutoffTs) {
                $status = 'expired'; // 보관기간 초과
            } elseif ($boundTs <= $nowTs) {
                $prevMonthTs = Carbon::now()->startOfMonth()->timestamp;
                $status = $boundTs <= $prevMonthTs ? 'archived' : 'current';
            } else {
                $status = 'upcoming'; // 미래 파티션
            }

            $items[] = [
                'name' => $p->PARTITION_NAME,
                'bound_raw' => $boundTs,
                'bound_date' => $boundDate,
                'rows' => $p->TABLE_ROWS,
                'status' => $status,
                'can_drop' => $status === 'expired',
            ];
        }

        return [
            'partitions' => $items,
            'total_partitions' => count($items),
            'total_rows' => $totalRows,
            'storage_mb' => $storageInfo->size_mb ?? 0,
            'retention_days' => $retentionDays,
        ];
    }

    /**
     * 미래 파티션 추가 (REORGANIZE PARTITION p_future)
     */
    public function addFuturePartitions(int $months): array
    {
        $dbName = config('database.connections.mysql.database');

        // 현재 파티션 바운드 조회
        $partitions = DB::select("
            SELECT PARTITION_NAME, PARTITION_DESCRIPTION
            FROM INFORMATION_SCHEMA.PARTITIONS
            WHERE TABLE_SCHEMA = ? AND TABLE_NAME = 'trigger_audit_logs'
            AND PARTITION_NAME IS NOT NULL
            ORDER BY PARTITION_ORDINAL_POSITION
        ", [$dbName]);

        $existingBounds = [];
        foreach ($partitions as $p) {
            if ($p->PARTITION_DESCRIPTION !== 'MAXVALUE') {
                $existingBounds[] = (int) $p->PARTITION_DESCRIPTION;
            }
        }

        $added = 0;
        $now = Carbon::now();

        for ($i = 0; $i <= $months; $i++) {
            $target = $now->copy()->addMonths($i)->startOfMonth()->addMonth();
            $ts = $target->timestamp;
            $name = 'p'.$target->copy()->subMonth()->format('Ym');

            if (in_array($ts, $existingBounds)) {
                continue;
            }

            DB::statement("ALTER TABLE trigger_audit_logs REORGANIZE PARTITION p_future INTO (
                PARTITION {$name} VALUES LESS THAN ({$ts}),
                PARTITION p_future VALUES LESS THAN MAXVALUE
            )");

            $added++;
        }

        if ($added === 0) {
            return ['success' => true, 'message' => '추가할 파티션이 없습니다 (이미 존재).'];
        }

        return ['success' => true, 'message' => "파티션 {$added}개 추가 완료."];
    }

    /**
     * 파티션 삭제 (보관기간 초과 검증 포함)
     */
    public function dropPartition(string $name): array
    {
        if ($name === 'p_future') {
            return ['success' => false, 'message' => 'p_future 파티션은 삭제할 수 없습니다.'];
        }

        $dbName = config('database.connections.mysql.database');
        $retentionDays = $this->getRetentionDays();

        // 파티션 존재 및 바운드 확인
        $partition = DB::selectOne("
            SELECT PARTITION_NAME, PARTITION_DESCRIPTION, TABLE_ROWS
            FROM INFORMATION_SCHEMA.PARTITIONS
            WHERE TABLE_SCHEMA = ? AND TABLE_NAME = 'trigger_audit_logs'
            AND PARTITION_NAME = ?
        ", [$dbName, $name]);

        if (! $partition) {
            return ['success' => false, 'message' => "파티션 '{$name}'을 찾을 수 없습니다."];
        }

        if ($partition->PARTITION_DESCRIPTION === 'MAXVALUE') {
            return ['success' => false, 'message' => '이 파티션은 삭제할 수 없습니다.'];
        }

        // 보관기간 초과 여부 서버측 검증
        $boundTs = (int) $partition->PARTITION_DESCRIPTION;
        $cutoffTs = Carbon::now()->subDays($retentionDays)->timestamp;

        if ($boundTs > $cutoffTs) {
            return ['success' => false, 'message' => '보관기간이 초과되지 않은 파티션은 삭제할 수 없습니다.'];
        }

        $rows = $partition->TABLE_ROWS;
        DB::statement("ALTER TABLE trigger_audit_logs DROP PARTITION {$name}");

        return ['success' => true, 'message' => "파티션 '{$name}' 삭제 완료 ({$rows}행)."];
    }

    /**
     * 보관기간 (일)
     */
    public function getRetentionDays(): int
    {
        return (int) env('AUDIT_RETENTION_DAYS', 395);
    }
}
