<?php

namespace App\Jobs;

use App\Models\Song;
use App\Models\Artist;
use App\Models\SongCategory;
use App\Models\SongLibraryCache;
use App\Enums\ArtistCategory;
use App\Enums\SongLanguageType;
use App\Enums\SongSituation;
use App\Helpers\ChineseNameConverter;
use App\Helpers\ChineseStrokesConverter;
use Illuminate\Bus\Queueable;
use Illuminate\Queue\SerializesModels;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Log;

class ImportSongChunkJob implements ShouldQueue
{
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;

    protected Collection $rows;
    protected string $id;
    protected array $categoryMap = [];
    protected array $artistCache = [];

    public function __construct(Collection $rows, string $id)
    {
        $this->rows = $rows;
        $this->id = $id;
        $this->categoryMap = SongCategory::pluck('id', 'code')->toArray();
    }

    public function handle(): void
    {
        Log::warning('匯入啟動', ['model' => "ImportSongChunkJob", 'rows_id' => $this->id]);

        $ToInsert = [];
        $songIdMap = [];
        $artistMap = [];
        $categoryMap = [];

        $pMap = [
            '\\\\SVR01\\DISK01\\' => 'DISK01\\',
            '\\\\SVR01\\DISK02\\' => 'DISK02\\',
            '\\\\SVR01\\DISK03\\' => 'DISK03\\',
            '\\\\SVR01\\DISK04\\' => 'DISK04\\',
            '\\\\SVR01\\DISK05\\' => 'DISK05\\',
            '\\\\SVR01\\DISK06\\' => 'DISK06\\',
            '\\\\SVR01\\DISK07\\' => 'DISK07\\',
            '\\\\SVR01\\DISK08\\' => 'DISK08\\',
            '\\\\SVR01\\DISK09\\' => 'DISK09\\',
        ];

        foreach ($this->rows as $index => $row) {
            $songId = trim($row['編號'] ?? '');
            if (!$songId || Song::where('id', $songId)->exists()) continue;

            try {
                $songName = $this->normalizeName($row['歌名'] ?? '');
                $simplified = ChineseNameConverter::convertToSimplified($songName);
                $phoneticAbbr = $row->has('注音') ? trim($row['注音'] ?? '') : ChineseNameConverter::getKTVZhuyinAbbr($simplified);
                $pinyinAbbr = $row->has('拼音') ? trim($row['拼音'] ?? '') : ChineseNameConverter::getKTVPinyinAbbr($simplified);

                $strokesAbbr = $row->has('kk3')
                    ? trim($row['kk3'] ?? 0)
                    : (preg_match('/\p{Han}/u', $songName[0] ?? '') ? ChineseStrokesConverter::getStrokes($songName[0]) : 0);

                $songNumber = $row->has('kk4')
                    ? trim($row['kk4'] ?? 0)
                    : mb_strlen($songName, 'UTF-8');

                $disk = $pMap[trim($row['路徑01'] ?? '')] ?? '';
                $filename = trim($row['檔名'] ?? '');

                $ToInsert[] = [
                    'id' => $songId,
                    'name' => $this->formatText($row['歌名']),
                    'adddate' => $this->parseExcelDate($row['日期'] ?? null),
                    'filename' => $disk . $filename,
                    'language_type' => SongLanguageType::fromLabelOrName($row['語別'] ?? ''),
                    'db_change' => trim($row['kk2'] ?? 0),
                    'vocal' => trim($row['kk6'] ?? 0),
                    'situation' => SongSituation::tryFrom(trim($row['kk7'] ?? '')) ?? SongSituation::Unset,
                    'copyright01' => trim($row['版權01'] ?? ''),
                    'copyright02' => trim($row['版權02'] ?? ''),
                    'note01' => trim($row['版權03'] ?? ''),
                    'note02' => trim($row['版權04'] ?? ''),
                    'note03' => trim($row['版權05'] ?? ''),
                    'note04' => trim($row['版權06'] ?? ''),
                    'enable' => trim($row['狀態'] ?? 1),
                    'simplified' => $simplified,
                    'phonetic_abbr' => $phoneticAbbr,
                    'pinyin_abbr' => $pinyinAbbr,
                    'strokes_abbr' => $strokesAbbr,
                    'song_number' => $songNumber,
                    'song_counts' => trim($row['點播次數'] ?? 0),
                ];

                foreach (['歌星A', '歌星B'] as $key) {
                    $artistName = $this->normalizeName($row[$key] ?? '');
                    if ($artistName === '' || ($key === '歌星B' && $artistName === $this->normalizeName($row['歌星A'] ?? ''))) continue;
                    $artistMap[$songId][] = $this->getOrCreateArtistId($artistName);
                }

                if (!empty($row['分類'])) {
                    foreach (explode(',', $row['分類']) as $code) {
                        $code = trim($code);
                        if (isset($this->categoryMap[$code])) {
                            $categoryMap[$songId][] = $this->categoryMap[$code];
                        }
                    }
                }

                $songIdMap[] = $songId;
            } catch (\Throwable $e) {
                Log::error("Row {$index} failed: {$e->getMessage()}", [
                    'row' => $row,
                    'trace' => $e->getTraceAsString()
                ]);
            }
        }

        // ✅ 批次插入
        collect($ToInsert)->chunk(500)->each(fn($chunk) => Song::insert($chunk->toArray()));

        // ✅ 一次撈資料並同步
        $songs = Song::whereIn('id', $songIdMap)->get()->keyBy('id');

        foreach ($songs as $songId => $song) {
            if (isset($artistMap[$songId])) {
                $song->artists()->sync($artistMap[$songId]);
            }
            if (isset($categoryMap[$songId])) {
                $song->categories()->sync($categoryMap[$songId]);
            }
            SongLibraryCache::syncFromSong($song);
        }
    }

    private function getOrCreateArtistId(string $name): int
    {
        if (isset($this->artistCache[$name])) {
            return $this->artistCache[$name];
        }

        $artist = Artist::firstOrCreate(['name' => $name], ['category' => ArtistCategory::Unset]);
        return $this->artistCache[$name] = $artist->id;
    }

    private function normalizeName(?string $str): string
    {
        return strtoupper(mb_convert_kana(trim($str ?? ''), 'as'));
    }

    protected function formatText($value)
    {
        if (is_numeric($value) && $value < 1 && $value > 0) {
            return \PhpOffice\PhpSpreadsheet\Shared\Date::excelToDateTimeObject($value)->format('H:i');
        }

        return trim((string)$value);
    }

    private function parseExcelDate($value): ?string
    {
        if (is_numeric($value)) {
            return \Carbon\Carbon::createFromFormat('Y-m-d', '1900-01-01')->addDays((int)$value - 2)->format('Y-m-d');
        }

        try {
            return \Carbon\Carbon::parse($value)->format('Y-m-d');
        } catch (\Exception) {
            return null;
        }
    }
}