<?php

namespace App\Console\Commands;

use App\Helpers\MigrationHelpers;
use Illuminate\Console\Attributes\Description;
use Illuminate\Console\Attributes\Signature;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\DB;

#[Signature('migrate:entries:execute {--limit=}')]
#[Description('Migrate WP entries')]
class MigrateEntriesExecute extends Command
{
    private const array WP_CPTS = ['translations', 'romhacks', 'homebrew', 'utilities', 'documents' ];

    private const array STATE_MAP = [
        'draft' => 'draft',
        'pending' => 'pending',
        'publish' => 'published',
        'private' => 'hidden',
        'locked' => 'locked',
    ];

    private const array ACF_FIELDS = [
        'entry_title', 'version_number', 'release_date', 'release_site',
        'youtube_video', 'staff', 'hashes',
    ];

    private const array MULTI_TAXONOMIES = [
        'language' => 'languages',
        'modifications' => 'modifications',
        'author-name' => 'authors',
        'document-category' => 'categories',
        'utility-category' => 'categories',
        'utility-os' => 'systems',
    ];

    private array $stats = [];

    private function getSingle( ?int $term_id, string $targetTable ): ?int
    {
        if (!$term_id) {
            return null;
        }

        return DB::table('migrations_logs')
            ->where('source_system', 'wp')
            ->where('target_table', $targetTable )
            ->where('source_id', $term_id)
            ->value('target_id');
    }

    private function uniqueSlug(string $baseSlug, string $table, ?int $ignoreId = null): string
    {
        $slug = $baseSlug;
        $i = 1;
        while (
            DB::table($table)->where('slug', $slug)
                ->when($ignoreId, fn ($q) => $q->where('id', '!=', $ignoreId))
                ->exists() && $i < 100
        ) {
            $slug = $baseSlug . '-' . $i++;
        }
        if ($i >= 100) {
            $slug = (string) \Str::uuid();
        }
        return $slug;
    }

    private function parseStaffCredits(?string $raw): array
    {
        if (!$raw || trim($raw) === '') {
            return [];
        }

        $credits = [];
        foreach (preg_split('/\r\n|\r|\n/', $raw) as $line) {
            $line = trim($line);
            if ($line === '') continue;

            if (preg_match('/^(.+?):\s*(.*)$/', $line, $m)) {
                $credits[] = ['name' => trim($m[1]), 'description' => trim($m[2])];
            } elseif (!empty($credits)) {
                $last = array_key_last($credits);
                $credits[$last]['description'] = trim($credits[$last]['description'] . ' ' . $line);
            } else {
                $credits[] = ['name' => $line, 'description' => ''];
            }
        }

        return $credits;
    }

    private function parseHashes(?string $raw): array
    {
        if (!$raw || trim($raw) === '') return [];

        $results = [];
        foreach (preg_split('/\n\s*\n/', trim($raw)) as $block) {
            $fields = [];
            foreach (preg_split('/\r\n|\r|\n/', trim($block)) as $line) {
                $line = trim($line);
                if ($line === '' || !str_contains($line, ':')) continue;
                [$key, $value] = array_map('trim', explode(':', $line, 2));
                $fields[strtolower(preg_replace('/[^a-z0-9]/i', '', $key))] = $value;
            }
            if (empty($fields)) continue;

            $results[] = [
                'filename' => $fields['filename'] ?? '',
                'hash_crc32' => $fields['crc32'] ?? '',
                'hash_sha1' => $fields['sha1'] ?? '',
            ];
        }
        return $results;
    }

        private function attachMany(int $entryId, string $pivotTable, string $foreignKey, array $ttids, string $targetTable): void
        {
            if (empty($ttids)) return;

            $ids = DB::table('migrations_logs')
                ->where('source_system', 'wp')->where('target_table', $targetTable)
                ->whereIn('source_id', $ttids)->pluck('target_id');

            foreach ($ids as $id) {
                DB::table($pivotTable)->insertOrIgnore(['entry_id' => $entryId, $foreignKey => $id]);
            }
        }

    /**
     * @param \StdClass $post
     * @param string $cpt
     *
     * @return void
     */
    private function migratePost( $post, string $cpt ): void
    {
        $exists = DB::table('migrations_logs')
            ->where('source_system', 'wp')
            ->where('source_table', 'wp_posts')
            ->where('source_id', $post->ID)
            ->exists();

        if ($exists)
            return;

        $meta = DB::connection('old_wp')
            ->table('postmeta')
            ->where('post_id', $post->ID)
            ->whereIn('meta_key', self::ACF_FIELDS)
            ->pluck('meta_value', 'meta_key');

        $terms = DB::connection('old_wp')
            ->table('term_relationships as tr')
            ->join('term_taxonomy as tt', 'tr.term_taxonomy_id', '=', 'tt.term_taxonomy_id')
            ->where('tr.object_id', $post->ID)
            ->whereIn('tt.taxonomy', array_merge(['game', 'platform', 'hack-status', 'experience-level'], array_keys(self::MULTI_TAXONOMIES)) )
            ->select('tt.taxonomy', 'tt.term_taxonomy_id')
            ->get();

        $byTax = [];
        foreach ($terms as $term) {
            $byTax[$term->taxonomy][] = $term->term_taxonomy_id;
        }

        $gameId = null;
        if( !empty( $byTax['game'][0] ) && !empty( $byTax['platform'][0] ) )
        {
            $gameId = DB::table('migration_game_plan')
                ->where('wp_game_id', $byTax['game'][0])
                ->where('wp_platform_id', $byTax['platform'][0])
                ->value('game_id');

            if( !$gameId )
                $this->stats['missing_game_plan']++;
        }

        $statusId = $this->getSingle( $byTax['hack-status'][0] ?? null, 'statuses' );
        $levelId = $this->getSingle( $byTax['experience-level'][0] ?? null, 'levels' );

        $userId = DB::table('migration_user_plan')
            ->where('wp_user_id', $post->post_author )
            ->value('user_id');

        if( !$userId ) {
            $this->stats['missing_author']++;
            return;
        }

        $title = $meta['entry_title'] ?? null;
        if( $cpt === 'translations' && !$title && $gameId )
        {
            $title = DB::table('games')->where('id', $gameId)->value('name');
        }

        $slug = $this->uniqueSlug( rawurldecode($post->post_name), 'entries' );

        $entryId = DB::table('entries')->insertGetId([
            'type' => $cpt,
            'title' => $title,
            'complete_title' => $post->post_title ?? null,
            'slug' => $slug,
            'description' => MigrationHelpers::htmlToMarkdown($post->post_content),
            'state' => self::STATE_MAP[$post->post_status],
            'game_id' => $gameId,
            'platform_id' => null,
            'status_id' => $statusId,
            'level_id' => $levelId,
            'version' => $meta['version_number'] ?? null,
            'release_date' => $meta['release_date'] ?? null,
            'staff_credits' => json_encode( $this->parseStaffCredits($meta['staff'] ?? null)),
            'relevant_link' => $meta['release_site'] ?? null,
            'youtube_link' => $meta['youtube_video'] ?? null,
            'user_id' => $userId,
            'created_at' => $post->post_date,
            'updated_at' => $post->post_modified,
        ]);

        $this->attachMany($entryId, 'entry_authors', 'author_id', $byTax['author-name'] ?? [], 'authors');
        $this->attachMany($entryId, 'entry_languages', 'language_id', $byTax['language'] ?? [], 'languages');

        if( $cpt === 'romhacks')
            $this->attachMany($entryId, 'entry_modifications', 'modification_id', $byTax['modifications'] ?? [], 'modifications');

        if ($cpt === 'utilities') {
            $this->attachMany($entryId, 'entry_categories', 'category_id', $byTax['utility-category'] ?? [], 'categories');
            $this->attachMany($entryId, 'entry_systems', 'system_id', $byTax['utility-os'] ?? [], 'systems');
        }
        if ($cpt === 'documents') {
            $this->attachMany($entryId, 'entry_categories', 'category_id', $byTax['document-category'] ?? [], 'categories');
        }

        if( $cpt === 'translations' || $cpt === 'romhacks' ){
            foreach ( $this->parseHashes( $meta['hashes'] ?? null ) as $hash ) {
                DB::table('entry_hashes')->insert([
                    'entry_id' => $entryId,
                    'filename' => $hash['filename'],
                    'hash_crc32' => $hash['hash_crc32'],
                    'hash_sha1' => $hash['hash_sha1'],
                    'verified' => 'TBD',
                    'created_at' => now(),
                    'updated_at' => now(),
                ]);
            }
        }

        DB::table('migrations_logs')->insert([
            'source_system' => 'wp',
            'source_table' => 'wp_posts',
            'source_id' => $post->ID,
            'target_table' => 'entries',
            'target_id' => $entryId,
            'status' => 'done',
            'migrated_at' => now(),
            'updated_at' => now(),
            'created_at' => now(),
        ]);

        $this->stats['created']++;

    }

    private function migrateCpt( string $cpt ): void
    {
        $this->info( "Current migration of : $cpt ");
        $this->stats = [ 'missing_author' => 0, 'missing_game_plan' => 0, 'created' => 0 ];

        $query = DB::connection('old_wp')
            ->table('posts')
            ->where('post_type', $cpt)
            ->whereIn('post_status', array_keys(self::STATE_MAP))
        ;

        if( $limit = $this->option('limit') ) {
            $query->limit((int) $limit);
        }

        $posts = $query->select('ID', 'post_title', 'post_name', 'post_content', 'post_status', 'post_author', 'post_date', 'post_modified')
            ->get();

        $this->withProgressBar($posts, fn( $post ) => $this->migratePost( $post, $cpt ) );

        $this->newLine();
        $this->info( "Created {$this->stats['created']} entries'. No authors {$this->stats['missing_author']}. Missing game {$this->stats['missing_game_plan']} entries" );
    }

    public function handle()
    {
        foreach ( self::WP_CPTS as $cpt ) {
            $this->migrateCpt( $cpt );
        }
    }
}
