#!/usr/bin/env php
<?php
/**
 * ptyMysqlRestore
 *
 * MySQL 데이터베이스 복원 도구
 * 설정 파일: ~/.ptyMysqlConfig.ini
 *
 * Usage: ./ptyMysqlRestore <pattern> [options]
 *        ./ptyMysqlRestore "*"           (현재 경로의 모든 sql 파일)
 *        ./ptyMysqlRestore "mydb.*.sql"  (mydb의 모든 테이블)
 */

namespace platyFramework;

require_once __DIR__ . '/ptyLibrary_PHP/cli/ptyCliOptionParser.php';
require_once __DIR__ . '/ptyLibrary_PHP/mysql/ptyMysqlConfig.php';

// 인자 파싱
$parsed = ptyCliOptionParser::parse($argv);
$positionalArgs = $parsed['positional'];
$options = $parsed['options'];
$mysqlSection = isset($options['mysql']) ? $options['mysql'] : 'default';
$verbose = isset($options['verbose']);
$force = isset($options['force']);
$inputDir = isset($options['input']) ? $options['input'] : '.';
$targetDatabase = isset($options['db']) ? $options['db'] : null;
$targetTable = isset($options['table']) ? $options['table'] : null;

// 도움말 또는 필수 인자 확인
if (empty($positionalArgs) || isset($options['help'])) {
    echo "사용법: {$argv[0]} <pattern> [옵션]\n";
    echo "\n";
    echo "인자:\n";
    echo "  <pattern>           파일 패턴 (* = 전체, mydb.*.sql = 특정 DB)\n";
    echo "\n";
    echo "옵션:\n";
    echo "  --mysql=섹션명      INI 파일 섹션 (기본값: default)\n";
    echo "  --db=DB명           복원할 대상 데이터베이스 (미지정시 파일명에서 추출)\n";
    echo "  --table=테이블명    복원할 대상 테이블명 (미지정시 파일명에서 추출)\n";
    echo "  --input=경로        입력 디렉토리 (기본값: 현재 디렉토리)\n";
    echo "  --force             확인 없이 바로 복원 실행\n";
    echo "  --verbose           상세 로그 출력\n";
    echo "  --help              도움말 출력\n";
    echo "\n";
    echo "예시:\n";
    echo "  {$argv[0]} \"*\"                          # 모든 sql/tgz 파일 복원\n";
    echo "  {$argv[0]} \"*.sql\"                      # 모든 sql 파일 복원\n";
    echo "  {$argv[0]} \"*.tgz\"                      # 모든 tgz 파일 복원 (압축 해제 후)\n";
    echo "  {$argv[0]} \"mydb.*.sql\"                 # mydb의 모든 테이블 복원\n";
    echo "  {$argv[0]} \"mydb.users.sql\"             # 특정 파일 복원\n";
    echo "  {$argv[0]} \"mydb.users.tgz\"             # 특정 tgz 파일 복원\n";
    echo "  {$argv[0]} \"*\" --input=/backup          # /backup 디렉토리에서 복원\n";
    echo "  {$argv[0]} \"*\" --mysql=production       # production 섹션 사용\n";
    echo "  {$argv[0]} \"*\" --force                  # 확인 없이 복원\n";
    echo "  {$argv[0]} \"*\" --db=mydb_dev            # 모든 파일을 mydb_dev DB에 복원\n";
    echo "  {$argv[0]} \"mydb.users.sql\" --table=users_backup  # users → users_backup 으로 복원\n";
    echo "\n";
    echo "파일명 형식: <database>.<table>.sql 또는 <database>.<table>.tgz\n";
    echo "\n";
    echo "설정 파일: ~/.ptyMysqlConfig.ini\n";
    echo ptyMysqlConfig::getConfigExample() . "\n";
    exit(isset($options['help']) ? 0 : 1);
}

$pattern = $positionalArgs[0];
$patterns = [];

// 패턴 정규화
if ($pattern === '*') {
    // * 이면 sql과 tgz 모두 검색
    $patterns = ['*.sql', '*.tgz'];
} elseif (preg_match('/\.(sql|tgz)$/i', $pattern)) {
    // 이미 확장자가 있으면 그대로 사용
    $patterns = [$pattern];
} else {
    // 확장자 없으면 .sql 추가
    $patterns = [$pattern . '.sql'];
}

// ANSI 색상 코드
$RED = "\033[1;31m";
$GREEN = "\033[1;32m";
$YELLOW = "\033[1;33m";
$CYAN = "\033[1;36m";
$MAGENTA = "\033[1;35m";
$RESET = "\033[0m";

// 타임스탬프 로그 함수
function logMessage($message, $verbose = false, $isVerbose = false) {
    if ($isVerbose && !$verbose) {
        return;
    }
    $timestamp = date('Y-m-d H:i:s');
    echo "[$timestamp] $message\n";
}

// 바이트를 읽기 쉬운 형식으로 변환
function formatBytes($bytes) {
    if ($bytes >= 1073741824) {
        return number_format($bytes / 1073741824, 2) . ' GB';
    } elseif ($bytes >= 1048576) {
        return number_format($bytes / 1048576, 2) . ' MB';
    } elseif ($bytes >= 1024) {
        return number_format($bytes / 1024, 2) . ' KB';
    } else {
        return $bytes . ' bytes';
    }
}

// 파일명에서 DB명, 테이블명 추출 (mydb.tablename.sql 또는 mydb.tablename.tgz -> [mydb, tablename])
function extractDbAndTable($filename) {
    $basename = basename($filename);
    $parts = explode('.', $basename);
    if (count($parts) >= 3) {
        $dbName = $parts[0];
        // 마지막 확장자(.sql 또는 .tgz) 제거하고 나머지를 테이블명으로
        array_pop($parts); // 확장자 제거
        array_shift($parts); // DB명 제거
        $tableName = implode('.', $parts);
        return [$dbName, $tableName];
    }
    return [null, null];
}

// ============================================
// SQL 파일 내 테이블명 변환 함수
// ============================================
//
// mysqldump로 생성된 SQL 파일에는 원본 테이블명이 하드코딩되어 있습니다.
// --table 옵션으로 다른 테이블명으로 복원할 경우, SQL 파일 내의 테이블명을 변경해야 합니다.
//
// 변환이 필요한 SQL 문:
//   - DROP TABLE IF EXISTS `src_table`   → DROP TABLE IF EXISTS `dst_table`
//   - CREATE TABLE `src_table`           → CREATE TABLE `dst_table`
//   - INSERT INTO `src_table`            → INSERT INTO `dst_table`
//   - LOCK TABLES `src_table`            → LOCK TABLES `dst_table`
//
// 주의: sed를 사용하여 스트리밍 처리 (대용량 파일도 메모리 문제 없음)
//
function transformTableName($srcFile, $srcTable, $dstTable, $verbose) {
    $dstFile = sys_get_temp_dir() . '/ptyMysqlRestore_' . getmypid() . '_' . time() . '.sql';

    $sedCmd = "sed";
    $sedCmd .= " -e " . escapeshellarg("s/DROP TABLE IF EXISTS \`{$srcTable}\`/DROP TABLE IF EXISTS \`{$dstTable}\`/g");
    $sedCmd .= " -e " . escapeshellarg("s/CREATE TABLE \`{$srcTable}\`/CREATE TABLE \`{$dstTable}\`/g");
    $sedCmd .= " -e " . escapeshellarg("s/INSERT INTO \`{$srcTable}\`/INSERT INTO \`{$dstTable}\`/g");
    $sedCmd .= " -e " . escapeshellarg("s/LOCK TABLES \`{$srcTable}\`/LOCK TABLES \`{$dstTable}\`/g");
    $sedCmd .= " " . escapeshellarg($srcFile);
    $sedCmd .= " > " . escapeshellarg($dstFile);

    if ($verbose) {
        logMessage("테이블명 변환: {$srcTable} → {$dstTable}", true);
    }

    exec($sedCmd, $output, $returnCode);

    if ($returnCode !== 0) {
        @unlink($dstFile);
        return null;
    }

    return $dstFile;
}

// ============================================
// tgz 파일 압축 해제 함수
// ============================================
function extractTgz($tgzFile, $verbose) {
    $tmpDir = sys_get_temp_dir() . '/ptyMysqlRestore_' . getmypid() . '_' . time();

    if (!mkdir($tmpDir, 0755, true)) {
        return null;
    }

    $tarCmd = "tar -xzf " . escapeshellarg($tgzFile) . " -C " . escapeshellarg($tmpDir);

    if ($verbose) {
        logMessage("압축 해제: $tarCmd", true);
    }

    exec($tarCmd, $output, $returnCode);

    if ($returnCode !== 0) {
        @rmdir($tmpDir);
        return null;
    }

    // 압축 해제된 SQL 파일 찾기
    $sqlFiles = glob($tmpDir . '/*.sql');
    if (empty($sqlFiles)) {
        @rmdir($tmpDir);
        return null;
    }

    return [
        'sqlFile' => $sqlFiles[0],
        'tmpDir' => $tmpDir
    ];
}

// 복원 실행 함수
function restoreFile($host, $user, $password, $dbName, $tableName, $sqlFile, $verbose) {
    $basename = basename($sqlFile);
    $size = filesize($sqlFile);
    $sizeStr = formatBytes($size);

    // mysql 명령 생성
    $cmd = "mysql";
    $cmd .= " -h " . escapeshellarg($host);
    $cmd .= " -u " . escapeshellarg($user);
    $cmd .= " -p" . escapeshellarg($password);
    $cmd .= " --default-character-set=utf8mb4";
    $cmd .= " " . escapeshellarg($dbName);
    $cmd .= " < " . escapeshellarg($sqlFile);
    $cmd .= " 2>&1";

    if ($verbose) {
        logMessage("명령: $cmd", $verbose, true);
    }

    exec($cmd, $output, $returnCode);

    if ($returnCode !== 0) {
        logMessage("ERROR: $tableName 복원 실패", true);
        if (!empty($output)) {
            logMessage(implode("\n", $output), true);
        }
        return false;
    }

    logMessage("OK: $tableName ($sizeStr)", true);
    return true;
}

try {
    // MySQL 연결 (database 설정 무시 - 여러 DB에 복원 가능)
    $conn = ptyMysqlConfig::connect($mysqlSection, false);
    $connection = $conn['connection'];
    $config = $conn['config'];

    // 입력 디렉토리 확인
    if (!is_dir($inputDir)) {
        throw new \Exception("입력 디렉토리가 없습니다: $inputDir");
    }

    // 패턴에 맞는 파일 찾기
    $files = [];
    foreach ($patterns as $p) {
        $searchPattern = rtrim($inputDir, '/') . '/' . $p;
        $found = glob($searchPattern);
        if ($found) {
            $files = array_merge($files, $found);
        }
    }
    $files = array_unique($files);

    if (empty($files)) {
        logMessage("패턴에 맞는 파일이 없습니다: " . implode(', ', $patterns), true);
        exit(0);
    }

    sort($files); // 파일명 순서로 정렬

    // ============================================
    // 복원 정보 표시
    // ============================================
    echo "\n";
    echo "{$CYAN}╔══════════════════════════════════════════════════════════════════╗{$RESET}\n";
    echo "{$CYAN}║                    MySQL 복원 정보 확인                          ║{$RESET}\n";
    echo "{$CYAN}╚══════════════════════════════════════════════════════════════════╝{$RESET}\n";
    echo "\n";

    // 서버 정보
    echo "{$YELLOW}[ 서버 정보 ]{$RESET}\n";
    echo str_repeat("-", 50) . "\n";
    echo "  Host     : {$config['host']}\n";
    echo "  User     : {$config['username']}\n";
    echo "  Section  : $mysqlSection\n";

    // MySQL 버전 조회
    $versionResult = mysqli_query($connection, "SELECT VERSION() as version");
    if ($versionResult && $row = mysqli_fetch_assoc($versionResult)) {
        echo "  Version  : {$row['version']}\n";
    }

    // 대상 DB 지정시 표시
    if ($targetDatabase) {
        echo "  {$CYAN}Target DB: $targetDatabase{$RESET}\n";
    }
    echo "\n";

    // 복원 대상 파일 분석
    $restoreInfo = [];
    $totalSize = 0;
    $dbList = [];

    foreach ($files as $sqlFile) {
        list($origDbName, $origTableName) = extractDbAndTable($sqlFile);

        if (!$origDbName) {
            continue;
        }

        // --db 옵션이 있으면 해당 DB로, 없으면 파일명에서 추출한 DB 사용
        $dbName = $targetDatabase !== null ? $targetDatabase : $origDbName;

        // --table 옵션이 있으면 해당 테이블로, 없으면 파일명에서 추출한 테이블 사용
        $tableName = $targetTable !== null ? $targetTable : $origTableName;

        $size = filesize($sqlFile);
        $totalSize += $size;

        if (!isset($dbList[$dbName])) {
            $dbList[$dbName] = [];
        }
        $dbList[$dbName][] = [
            'table' => $tableName,
            'origTable' => $origTableName,
            'file' => $sqlFile,
            'size' => $size,
            'origDb' => $origDbName,
            'isTgz' => preg_match('/\.tgz$/i', $sqlFile),
        ];
    }

    // DB별 복원 대상 표시
    echo "{$YELLOW}[ 복원 대상 ]{$RESET}\n";
    echo str_repeat("-", 50) . "\n";
    echo "  Input    : " . realpath($inputDir) . "\n";
    echo "  Pattern  : " . implode(', ', $patterns) . "\n";
    echo "  Files    : " . count($files) . "개\n";
    echo "  Total    : " . formatBytes($totalSize) . "\n";
    echo "\n";

    foreach ($dbList as $dbName => $tables) {
        // DB 존재 여부 확인
        $checkDb = mysqli_query($connection, "SHOW DATABASES LIKE '$dbName'");
        $dbExists = mysqli_num_rows($checkDb) > 0;
        $dbStatus = $dbExists ? "{$GREEN}[EXISTS]{$RESET}" : "{$YELLOW}[NEW]{$RESET}";

        echo "{$CYAN}  Database: $dbName{$RESET} $dbStatus\n";

        foreach ($tables as $info) {
            $tableName = $info['table'];
            $origTableName = $info['origTable'];
            $sizeStr = formatBytes($info['size']);

            // 원본 정보 표시 (DB나 테이블이 다를 경우)
            $origInfo = "";
            $origParts = [];
            if ($targetDatabase && $info['origDb'] !== $dbName) {
                $origParts[] = $info['origDb'];
            }
            if ($targetTable && $origTableName !== $tableName) {
                $origParts[] = $origTableName;
            }
            if (!empty($origParts)) {
                $origInfo = " {$MAGENTA}← " . implode('.', $origParts) . "{$RESET}";
            }

            // 테이블 존재 여부 확인
            $tableStatus = "";
            if ($dbExists) {
                $checkTable = mysqli_query($connection, "SHOW TABLES FROM `$dbName` LIKE '$tableName'");
                if ($checkTable && mysqli_num_rows($checkTable) > 0) {
                    $tableStatus = "{$RED}[OVERWRITE]{$RESET}";
                } else {
                    $tableStatus = "{$GREEN}[NEW]{$RESET}";
                }
            } else {
                $tableStatus = "{$GREEN}[NEW]{$RESET}";
            }

            echo "    - $tableName ($sizeStr) $tableStatus$origInfo\n";
        }
        echo "\n";
    }

    // 경고 메시지
    echo "{$RED}╔══════════════════════════════════════════════════════════════════╗{$RESET}\n";
    echo "{$RED}║  주의: [OVERWRITE] 표시된 테이블은 기존 데이터가 삭제됩니다!    ║{$RESET}\n";
    echo "{$RED}╚══════════════════════════════════════════════════════════════════╝{$RESET}\n";
    echo "\n";

    // 확인 절차 (--force가 없을 때만)
    if (!$force) {
        echo "복원을 진행하시겠습니까? (y/N): ";
        $handle = fopen("php://stdin", "r");
        $line = fgets($handle);
        fclose($handle);

        $answer = strtolower(trim($line));
        if ($answer !== 'y' && $answer !== 'yes') {
            echo "\n복원이 취소되었습니다.\n";
            mysqli_close($connection);
            exit(0);
        }
        echo "\n";
    }

    // ============================================
    // 복원 실행
    // ============================================
    echo str_repeat("=", 60) . "\n";
    logMessage("MySQL 복원 시작", true);
    echo str_repeat("=", 60) . "\n";

    $totalRestores = 0;
    $successRestores = 0;

    foreach ($dbList as $dbName => $tables) {
        // DB 존재 확인 및 생성
        $checkDb = mysqli_query($connection, "SHOW DATABASES LIKE '$dbName'");
        if (mysqli_num_rows($checkDb) === 0) {
            logMessage("DB 생성: $dbName", true);
            mysqli_query($connection, "CREATE DATABASE IF NOT EXISTS `$dbName` DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci");
        }

        foreach ($tables as $info) {
            $totalRestores++;
            $tableName = $info['table'];
            $origTableName = $info['origTable'];
            $sqlFile = $info['file'];
            $tempFile = null;
            $extractedInfo = null;

            // .tgz 파일인 경우 압축 해제
            if ($info['isTgz']) {
                $extractedInfo = extractTgz($sqlFile, $verbose);
                if ($extractedInfo === null) {
                    logMessage("ERROR: {$tableName} 압축 해제 실패", true);
                    continue;
                }
                $sqlFile = $extractedInfo['sqlFile'];
            }

            // 테이블명이 다를 경우 sed로 변환
            if ($tableName !== $origTableName) {
                $tempFile = transformTableName($sqlFile, $origTableName, $tableName, $verbose);
                if ($tempFile === null) {
                    logMessage("ERROR: 테이블명 변환 실패 - $origTableName → $tableName", true);
                    // 압축 해제된 임시 파일 정리
                    if ($extractedInfo !== null) {
                        @unlink($extractedInfo['sqlFile']);
                        @rmdir($extractedInfo['tmpDir']);
                    }
                    continue;
                }
                $sqlFile = $tempFile;
            }

            if (restoreFile($config['host'], $config['username'], $config['password'], $dbName, $tableName, $sqlFile, $verbose)) {
                $successRestores++;
            }

            // 임시 파일 정리
            if ($tempFile !== null && file_exists($tempFile)) {
                @unlink($tempFile);
            }

            // 압축 해제된 임시 파일/디렉토리 정리
            if ($extractedInfo !== null) {
                @unlink($extractedInfo['sqlFile']);
                @rmdir($extractedInfo['tmpDir']);
            }
        }
    }

    mysqli_close($connection);

    echo "\n" . str_repeat("=", 60) . "\n";
    logMessage("복원 완료: $successRestores / $totalRestores", true);

    if ($successRestores < $totalRestores) {
        exit(1);
    }

} catch (\Exception $e) {
    echo "Error: " . $e->getMessage() . "\n";
    exit(1);
}
