<?php

namespace App\Console\Commands;

use Illuminate\Console\Command;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Str;

class ImportExportTags extends Command
{
    protected $signature = 'tags:import-export
                            {action : Action to perform: import, export-categories, export-tags, remove-group}
                            {file? : Specific JSON file path (optional for import - will process all files in imports/tags/ if not specified)}
                            {--category-id= : Export tags for specific category ID only}
                            {--locale= : Export tags for specific locale only}
                            {--output= : Output file name (optional, defaults to generated name)}
                            {--dry-run : Preview import without making changes}';

    protected $description = 'Import/export tags and categories in JSON format for AI tag generation';

    protected array $supportedLocales = ['en', 'nl', 'fr', 'es', 'de'];

    public function handle()
    {
        $action = $this->argument('action');

        switch ($action) {
            case 'import':
                return $this->importTags();
            case 'export-categories':
                return $this->exportCategories();
            case 'export-tags':
                return $this->exportTags();
            case 'remove-group':
                return $this->removeTagGroups();
            default:
                $this->error("Invalid action. Use: import, export-categories, export-tags, or remove-group");
                return 1;
        }
    }

    /**
     * Import tags from JSON file(s)
     */
    protected function importTags(): int
    {
        $filePath = $this->argument('file');
        $dryRun = $this->option('dry-run');

        // If no specific file provided, process all files in imports/tags/ folder
        if (!$filePath) {
            return $this->importFromFolder($dryRun);
        }

        // Process single file
        return $this->importSingleFile($filePath, $dryRun);
    }

    /**
     * Import all JSON files from imports/tags/ folder
     */
    protected function importFromFolder(bool $dryRun): int
    {
        $importFolder = 'imports/tags';

        // Create folder if it doesn't exist
        if (!is_dir($importFolder)) {
            mkdir($importFolder, 0755, true);
            $this->info("Created imports folder: {$importFolder}");
            $this->info("Please place your JSON files in this folder and run the command again.");
            return 0;
        }

        // Get all JSON files from the folder
        $jsonFiles = glob($importFolder . '/*.json');

        if (empty($jsonFiles)) {
            $this->warn("No JSON files found in {$importFolder}/");
            $this->info("Please place your JSON files in this folder and run the command again.");
            return 0;
        }

        $this->info("Found " . count($jsonFiles) . " JSON files to process:");
        foreach ($jsonFiles as $file) {
            $this->line("  - " . basename($file));
        }

        if ($dryRun) {
            $this->warn("DRY RUN MODE - No changes will be made");
        }

        $this->newLine();

        $totalImported = 0;
        $totalSkipped = 0;
        $totalErrors = 0;
        $processedFiles = 0;
        $failedFiles = 0;

        foreach ($jsonFiles as $filePath) {
            $fileName = basename($filePath);
            $this->info("Processing file: {$fileName}");
            $this->line(str_repeat('=', 50));

            try {
                $result = $this->importSingleFile($filePath, $dryRun);

                if ($result === 0) {
                    $processedFiles++;
                    // Get the stats from the last import (we'll need to modify importSingleFile to return stats)
                } else {
                    $failedFiles++;
                    $this->error("Failed to process {$fileName}");
                }
            } catch (\Exception $e) {
                $failedFiles++;
                $this->error("Error processing {$fileName}: " . $e->getMessage());
            }

            $this->newLine();
        }

        // Final summary
        $this->info("Overall Import Summary:");
        $this->line("  Files processed successfully: {$processedFiles}");
        $this->line("  Files failed: {$failedFiles}");
        $this->line("  Total files: " . count($jsonFiles));

        return $failedFiles > 0 ? 1 : 0;
    }

    /**
     * Import tags from a single JSON file
     */
    protected function importSingleFile(string $filePath, bool $dryRun): int
    {
        if (!file_exists($filePath)) {
            $this->error("File not found: {$filePath}");
            return 1;
        }

        $jsonContent = file_get_contents($filePath);
        $data = json_decode($jsonContent, true);

        if (json_last_error() !== JSON_ERROR_NONE) {
            $this->error('Invalid JSON format: ' . json_last_error_msg());
            return 1;
        }

        if (!isset($data['tags']) || !is_array($data['tags'])) {
            $this->error('JSON must contain a "tags" array');
            return 1;
        }

        $this->info("Found " . count($data['tags']) . " tags to import from " . basename($filePath));

        $imported = 0;
        $skipped = 0;
        $errors = 0;

        foreach ($data['tags'] as $index => $tagData) {
            try {
                $result = $this->importSingleTag($tagData, $index + 1, $dryRun);

                if ($result === 'imported') {
                    $imported++;
                } elseif ($result === 'skipped') {
                    $skipped++;
                }
            } catch (\Exception $e) {
                $this->error("Error processing tag at index " . ($index + 1) . ": " . $e->getMessage());
                $errors++;
            }
        }

        $this->newLine();
        $this->info("File Summary for " . basename($filePath) . ":");
        $this->line("  Imported: {$imported}");
        $this->line("  Skipped: {$skipped}");
        $this->line("  Errors: {$errors}");

        // Move processed file to processed folder (only if not dry run and no errors)
        if (!$dryRun && $errors === 0) {
            $this->moveProcessedFile($filePath);
        }

        return $errors > 0 ? 1 : 0;
    }

    /**
     * Move processed file to processed folder
     */
    protected function moveProcessedFile(string $filePath): void
    {
        $processedFolder = 'imports/tags/processed';

        if (!is_dir($processedFolder)) {
            mkdir($processedFolder, 0755, true);
        }

        $fileName = basename($filePath);
        $timestamp = now()->format('Y-m-d-H-i-s');
        $newFileName = pathinfo($fileName, PATHINFO_FILENAME) . "_{$timestamp}.json";
        $newPath = $processedFolder . '/' . $newFileName;

        if (rename($filePath, $newPath)) {
            $this->line("  ✓ Moved processed file to: {$newPath}");
        } else {
            $this->warn("  Could not move processed file to processed folder");
        }
    }

    /**
     * Import a single tag from the JSON data
     */
    protected function importSingleTag(array $tagData, int $index, bool $dryRun): string
    {
        // Validate required fields
        if (!isset($tagData['translations']) || !is_array($tagData['translations'])) {
            throw new \InvalidArgumentException("Tag {$index}: 'translations' field is required and must be an array");
        }

        if (!isset($tagData['category'])) {
            throw new \InvalidArgumentException("Tag {$index}: 'category' field is required");
        }

        $category = $tagData['category'];

        // Verify category exists
        $categoryId = null;

        if (isset($category['id'])) {
            $categoryId = $category['id'];
        } elseif (isset($category['name'])) {
            // Find category by name
            $categoryRecord = DB::table('category_translations')
                ->join('categories', 'category_translations.category_id', '=', 'categories.id')
                ->where('category_translations.name', $category['name'])
                ->where('category_translations.locale', 'en')
                ->where('categories.type', 'App\\Models\\Tag')
                ->first();

            if (!$categoryRecord) {
                throw new \InvalidArgumentException("Tag {$index}: Category '{$category['name']}' not found");
            }

            $categoryId = $categoryRecord->category_id;
        } else {
            throw new \InvalidArgumentException("Tag {$index}: Category must have either 'id' or 'name' field");
        }

        // Verify category exists
        $categoryExists = DB::table('categories')
            ->where('id', $categoryId)
            ->where('type', 'App\\Models\\Tag')
            ->exists();

        if (!$categoryExists) {
            throw new \InvalidArgumentException("Tag {$index}: Category with ID {$categoryId} not found");
        }

        // Check if this exact tag group already exists
        $existingTagGroup = $this->findExistingTagGroup($tagData['translations'], $categoryId);

        if ($existingTagGroup) {
            $this->line("  Skipping existing tag group with translations: " . implode(', ', array_values($tagData['translations'])));
            return 'skipped';
        }

        if ($dryRun) {
            $this->line("  Would create new tag group for category {$categoryId}:");
            foreach ($tagData['translations'] as $locale => $tagName) {
                if (in_array($locale, $this->supportedLocales)) {
                    $this->line("    {$locale}: '{$tagName}'");
                }
            }
            return 'imported';
        }

        // Create new context for this tag group (each imported tag group gets its own context)
        $contextId = DB::table('taggable_contexts')->insertGetId([
            'category_id' => $categoryId,
            'updated_by_user' => 1,
            'created_at' => now(),
            'updated_at' => now(),
        ]);

        $this->line("  Created new context {$contextId} for category {$categoryId}");

        $createdTags = [];

        // Process each translation - each gets its own tag_id
        foreach ($tagData['translations'] as $locale => $tagName) {
            if (!in_array($locale, $this->supportedLocales)) {
                $this->warn("  Skipping unsupported locale: {$locale}");
                continue;
            }

            // Create the tag
            $tagId = DB::table('taggable_tags')->insertGetId([
                'name' => $tagName,
                'normalized' => Str::lower($tagName),
                'created_at' => now(),
                'updated_at' => now(),
            ]);

            // Create locale record
            DB::table('taggable_locales')->insert([
                'taggable_tag_id' => $tagId,
                'locale' => $locale,
                'comment' => '',
                'updated_by_user' => 1,
                'created_at' => now(),
                'updated_at' => now(),
            ]);

            // Link to the same context (this creates the relationship between all translations)
            DB::table('taggable_locale_context')->insert([
                'tag_id' => $tagId,
                'context_id' => $contextId,
            ]);

            $createdTags[] = ['locale' => $locale, 'name' => $tagName, 'id' => $tagId];
            $this->line("  ✓ Created tag: '{$tagName}' ({$locale}) with ID {$tagId}");
        }

        $this->line("  ✓ Created tag group with " . count($createdTags) . " translations in context {$contextId}");

        return 'imported';
    }

    /**
     * Check if this exact tag group already exists
     */
    protected function findExistingTagGroup(array $translations, int $categoryId): ?int
    {
        // Look for any existing tag with any of these names in the same category
        foreach ($translations as $locale => $tagName) {
            if (!in_array($locale, $this->supportedLocales)) {
                continue;
            }

            $existingTag = DB::table('taggable_tags as tt')
                ->join('taggable_locales as tl', 'tt.tag_id', '=', 'tl.taggable_tag_id')
                ->join('taggable_locale_context as tlc', 'tt.tag_id', '=', 'tlc.tag_id')
                ->join('taggable_contexts as tc', 'tlc.context_id', '=', 'tc.id')
                ->where('tt.name', $tagName)
                ->where('tl.locale', $locale)
                ->where('tc.category_id', $categoryId)
                ->first();

            if ($existingTag) {
                // Found an existing tag, now check if the context has all the same translations
                $contextId = $existingTag->context_id;

                $existingTranslations = DB::table('taggable_tags as tt')
                    ->join('taggable_locales as tl', 'tt.tag_id', '=', 'tl.taggable_tag_id')
                    ->join('taggable_locale_context as tlc', 'tt.tag_id', '=', 'tlc.tag_id')
                    ->where('tlc.context_id', $contextId)
                    ->pluck('tt.name', 'tl.locale')
                    ->toArray();

                // Check if existing translations match exactly
                $inputTranslationsFiltered = array_intersect_key($translations, array_flip($this->supportedLocales));

                if (
                    count(array_diff_assoc($inputTranslationsFiltered, $existingTranslations)) === 0 &&
                    count(array_diff_assoc($existingTranslations, $inputTranslationsFiltered)) === 0
                ) {
                    return $contextId;
                }
            }
        }

        return null;
    }

    /**
     * Export categories to JSON format for AI tag generation
     */
    protected function exportCategories(): int
    {
        $this->info('Exporting categories...');

        // Create export folder if it doesn't exist
        $exportFolder = 'exports/categories';
        if (!is_dir($exportFolder)) {
            mkdir($exportFolder, 0755, true);
            $this->info("Created export folder: {$exportFolder}");
        }

        $categories = DB::table('categories')
            ->join('category_translations', 'categories.id', '=', 'category_translations.category_id')
            ->where('categories.type', 'App\\Models\\Tag')
            ->where('category_translations.locale', 'en')
            ->select(
                'categories.id',
                'category_translations.name',
                'category_translations.slug',
                'categories.color'
            )
            ->orderBy('category_translations.name')
            ->get();

        $exportData = [
            'metadata' => [
                'exported_at' => now()->toISOString(),
                'total_categories' => $categories->count(),
                'purpose' => 'AI tag generation input',
                'supported_locales' => $this->supportedLocales,
                'instructions' => [
                    'Please generate tags for the categories below',
                    'Follow the example_format exactly',
                    'Include translations for all supported locales',
                    'Use the category id and name provided',
                    'Generate 10-20 relevant tags per category',
                ],
            ],
            'categories' => $categories->map(function ($category) {
                return [
                    'id' => $category->id,
                    'name' => $category->name,
                    'slug' => $category->slug,
                    'color' => $category->color,
                ];
            })->toArray(),
            'example_format' => [
                'tags' => [
                    [
                        'translations' => [
                            'en' => 'Example English Tag',
                            'nl' => 'Voorbeeld Nederlandse Tag',
                            'fr' => 'Exemple de Tag Français',
                            'es' => 'Ejemplo de Etiqueta Española',
                            'de' => 'Beispiel Deutsche Tag',
                        ],
                        'category' => [
                            'id' => 1,
                            'name' => 'Category Name'
                        ]
                    ]
                ]
            ]
        ];

        $fileName = $this->option('output') ?: 'categories-for-ai-' . now()->format('Y-m-d-H-i-s') . '.json';
        $fullPath = $exportFolder . '/' . $fileName;

        file_put_contents($fullPath, json_encode($exportData, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE));

        $this->info("✓ Exported {$categories->count()} categories to: {$fullPath}");
        $this->newLine();
        $this->info("Next steps:");
        $this->line("1. Send the exported file to an AI service");
        $this->line("2. Ask AI to generate tags following the example_format");
        $this->line("3. Save AI response as JSON file(s) in imports/tags/ folder");
        $this->line("4. Run: php artisan tags:import-export import");
        $this->newLine();
        $this->info("Folder structure:");
        $this->line("  exports/categories/ - Generated category files for AI");
        $this->line("  imports/tags/ - Place AI-generated tag files here");
        $this->line("  imports/tags/processed/ - Successfully processed files are moved here");

        return 0;
    }

    /**
     * Interactive removal of tag groups
     */
    protected function removeTagGroups(): int
    {
        $this->info('Interactive Tag Group Removal');
        $this->newLine();
        $this->info('This tool allows you to remove entire tag groups (all translations of the same concept).');
        $this->warn('⚠️  You can press Ctrl+C at any time to abort this script.');
        $this->newLine();

        $removedGroups = 0;

        while (true) {
            $tagId = $this->ask('Enter a taggable_tag_id to remove its entire group (or "exit" to quit)');

            if (strtolower(trim($tagId)) === 'exit') {
                break;
            }

            if (!is_numeric($tagId) || $tagId <= 0) {
                $this->error('Please enter a valid numeric tag ID.');
                continue;
            }

            $tagId = (int) $tagId;

            try {
                $result = $this->removeTagGroup($tagId);
                if ($result) {
                    $removedGroups++;
                }
            } catch (\Exception $e) {
                $this->error('Error: ' . $e->getMessage());
            }

            $this->newLine();
        }

        $this->newLine();
        $this->info("Session summary: Removed {$removedGroups} tag groups.");

        return 0;
    }

    /**
     * Remove a single tag group by tag ID
     */
    protected function removeTagGroup(int $tagId): bool
    {
        // Find the tag and its context
        $tagInfo = DB::table('taggable_tags as tt')
            ->join('taggable_locales as tl', 'tt.tag_id', '=', 'tl.taggable_tag_id')
            ->join('taggable_locale_context as tlc', 'tt.tag_id', '=', 'tlc.tag_id')
            ->join('taggable_contexts as tc', 'tlc.context_id', '=', 'tc.id')
            ->join('categories as c', 'tc.category_id', '=', 'c.id')
            ->join('category_translations as ct', function ($join) {
                $join->on('c.id', '=', 'ct.category_id')
                    ->where('ct.locale', '=', 'en');
            })
            ->where('tt.tag_id', $tagId)
            ->select(
                'tt.tag_id',
                'tt.name',
                'tl.locale',
                'tc.id as context_id',
                'c.id as category_id',
                'ct.name as category_name'
            )
            ->first();

        if (!$tagInfo) {
            $this->error("Tag with ID {$tagId} not found.");
            return false;
        }

        // Get all tags in the same context (the entire tag group)
        $tagGroup = DB::table('taggable_tags as tt')
            ->join('taggable_locales as tl', 'tt.tag_id', '=', 'tl.taggable_tag_id')
            ->join('taggable_locale_context as tlc', 'tt.tag_id', '=', 'tlc.tag_id')
            ->where('tlc.context_id', $tagInfo->context_id)
            ->select(
                'tt.tag_id',
                'tt.name',
                'tl.locale'
            )
            ->orderBy('tl.locale')
            ->get();

        if ($tagGroup->isEmpty()) {
            $this->error("No tag group found for tag ID {$tagId}.");
            return false;
        }

        // Display the tag group information
        $this->info("Found tag group in category: {$tagInfo->category_name}");
        $this->info("Context ID: {$tagInfo->context_id}");
        $this->newLine();
        $this->warn("The following tags will be PERMANENTLY removed:");

        foreach ($tagGroup as $tag) {
            $this->line("  • ID {$tag->tag_id}: '{$tag->name}' ({$tag->locale})");
        }

        $this->newLine();
        $confirmMessage = "Are you sure you want to remove this entire tag group ({$tagGroup->count()} tags)?";

        if (!$this->confirm($confirmMessage, false)) {
            $this->info('Removal cancelled.');
            return false;
        }

        // Perform the removal
        $this->info('Removing tag group...');

        try {
            DB::transaction(function () use ($tagGroup, $tagInfo) {
                $tagIds = $tagGroup->pluck('tag_id')->toArray();

                // Remove context links
                DB::table('taggable_locale_context')
                    ->whereIn('tag_id', $tagIds)
                    ->delete();

                // Remove taggable relationships (if any exist)
                DB::table('taggable_taggables')
                    ->whereIn('tag_id', $tagIds)
                    ->delete();

                // Remove locale records
                DB::table('taggable_locales')
                    ->whereIn('taggable_tag_id', $tagIds)
                    ->delete();

                // Remove the tags themselves
                DB::table('taggable_tags')
                    ->whereIn('tag_id', $tagIds)
                    ->delete();

                // Remove the context if it's now empty
                $remainingTags = DB::table('taggable_locale_context')
                    ->where('context_id', $tagInfo->context_id)
                    ->count();

                if ($remainingTags === 0) {
                    DB::table('taggable_contexts')
                        ->where('id', $tagInfo->context_id)
                        ->delete();

                    $this->line("  ✓ Removed empty context {$tagInfo->context_id}");
                }
            });

            $this->info("✓ Successfully removed tag group ({$tagGroup->count()} tags)");

            // Show what was removed
            foreach ($tagGroup as $tag) {
                $this->line("  ✓ Removed: '{$tag->name}' ({$tag->locale}) - ID {$tag->tag_id}");
            }

            return true;
        } catch (\Exception $e) {
            $this->error('Failed to remove tag group: ' . $e->getMessage());
            return false;
        }
    }

    /**
     * Find existing tag group helper method

    /**
     * Export existing tags to JSON format
     */
    protected function exportTags(): int
    {
        $categoryId = $this->option('category-id');
        $locale = $this->option('locale');

        $this->info('Exporting tags...');

        // Create export folder if it doesn't exist
        $exportFolder = 'exports/tags';
        if (!is_dir($exportFolder)) {
            mkdir($exportFolder, 0755, true);
            $this->info("Created export folder: {$exportFolder}");
        }

        $query = DB::table('taggable_tags as tt')
            ->join('taggable_locales as tl', 'tt.tag_id', '=', 'tl.taggable_tag_id')
            ->join('taggable_locale_context as tlc', 'tt.tag_id', '=', 'tlc.tag_id')
            ->join('taggable_contexts as tc', 'tlc.context_id', '=', 'tc.id')
            ->join('categories as c', 'tc.category_id', '=', 'c.id')
            ->join('category_translations as ct', function ($join) {
                $join->on('c.id', '=', 'ct.category_id')
                    ->where('ct.locale', '=', 'en');
            })
            ->select(
                'tt.tag_id',
                'tt.name',
                'tl.locale',
                'tc.id as context_id',
                'c.id as category_id',
                'ct.name as category_name'
            );

        if ($categoryId) {
            $query->where('c.id', $categoryId);
        }

        if ($locale) {
            $query->where('tl.locale', $locale);
        }

        $tags = $query->orderBy('c.id')
            ->orderBy('tc.id')
            ->orderBy('tl.locale')
            ->get();

        // Group tags by context (since each context now contains one tag group)
        $contextGroups = [];

        foreach ($tags as $tag) {
            $contextId = $tag->context_id;

            if (!isset($contextGroups[$contextId])) {
                $contextGroups[$contextId] = [
                    'category_id' => $tag->category_id,
                    'category_name' => $tag->category_name,
                    'translations' => [],
                ];
            }

            // Add this translation to the context group
            $contextGroups[$contextId]['translations'][$tag->locale] = $tag->name;
        }

        // Convert to export format - each context becomes one tag group
        $exportTags = [];
        foreach ($contextGroups as $contextId => $contextData) {
            // Skip contexts that don't have any translations (shouldn't happen, but safety check)
            if (empty($contextData['translations'])) {
                continue;
            }

            // If filtering by locale, only include contexts that have that locale
            if ($locale && !isset($contextData['translations'][$locale])) {
                continue;
            }

            $exportTags[] = [
                'translations' => $contextData['translations'],
                'category' => [
                    'id' => $contextData['category_id'],
                    'name' => $contextData['category_name'],
                ],
                '_metadata' => [
                    'context_id' => $contextId,
                    'translation_count' => count($contextData['translations']),
                ],
            ];
        }

        $exportData = [
            'metadata' => [
                'exported_at' => now()->toISOString(),
                'total_tag_groups' => count($exportTags),
                'total_contexts' => count($contextGroups),
                'filters' => [
                    'category_id' => $categoryId,
                    'locale' => $locale,
                ],
                'structure_info' => [
                    'each_tag_group' => 'represents one context with 1-5 translations',
                    'context_per_concept' => 'each context contains translations of the same concept',
                    'max_translations_per_group' => 5,
                    'supported_locales' => $this->supportedLocales,
                ],
            ],
            'tags' => $exportTags,
        ];

        $fileName = $this->option('output') ?: 'tags-backup-' . now()->format('Y-m-d-H-i-s') . '.json';

        if ($categoryId) {
            $fileName = str_replace('.json', "-category-{$categoryId}.json", $fileName);
        }

        if ($locale) {
            $fileName = str_replace('.json', "-{$locale}.json", $fileName);
        }

        $fullPath = $exportFolder . '/' . $fileName;
        file_put_contents($fullPath, json_encode($exportData, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE));

        $this->info("✓ Exported " . count($exportTags) . " tag groups from " . count($contextGroups) . " contexts to: {$fullPath}");
        $this->newLine();

        // Show some statistics
        $translationCounts = array_count_values(array_map(fn($tag) => count($tag['translations']), $exportTags));
        $this->info("Translation distribution:");
        foreach ($translationCounts as $count => $groups) {
            $this->line("  {$count} translation(s): {$groups} tag groups");
        }

        if ($locale) {
            $this->newLine();
            $this->info("Note: When filtering by locale '{$locale}', only contexts containing that locale are included.");
            $this->info("Each exported tag group shows all translations for that context, not just the filtered locale.");
        }

        return 0;
    }
}
