<?php

namespace App\Console\Commands;

use App\Models\Client\Client;
use App\Models\Document\DocumentType;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\File;
use Illuminate\Support\Facades\Storage;
use SplFileInfo;

class ImportFolderDocuments extends Command {
    protected $signature = 'documents:import-folder
                            {path : Absolute path to the root folder (contains 2025/ and 2026/ subfolders)}
                            {--dry-run : Log what would be imported without writing anything}';

    protected $description = 'Import client documents from a year/month/client/doctype folder structure.';

    private int   $clientsFound   = 0;
    private array $clientsSkipped = [];
    private int   $filesImported  = 0;

    public function handle(): int {
        $rootPath = $this->argument('path');
        $dry      = (bool) $this->option('dry-run');

        if (!File::isDirectory($rootPath)) {
            $this->error("Path [{$rootPath}] does not exist or is not a directory.");
            return 1;
        }

        if ($dry) {
            $this->warn('DRY RUN — no files or records will be written.');
        }

        foreach (['2025', '2026'] as $year) {
            $yearPath = rtrim($rootPath, '/\\') . DIRECTORY_SEPARATOR . $year;

            if (!File::isDirectory($yearPath)) {
                $this->line("  Year [{$year}] not found, skipping.");
                continue;
            }

            $this->info("Processing year: {$year}");

            foreach (File::directories($yearPath) as $monthPath) {
                $month = basename($monthPath);
                $this->comment("  Month: {$month}");

                foreach (File::directories($monthPath) as $clientPath) {
                    $this->processClientFolder(new SplFileInfo($clientPath), $dry);
                }
            }
        }

        $this->newLine();
        $this->info("Done.");
        $this->table(
            ['Clients found', 'Clients skipped', 'Files imported'],
            [[$this->clientsFound, count($this->clientsSkipped), $this->filesImported]]
        );

        if (!empty($this->clientsSkipped)) {
            $this->newLine();
            $this->warn('Skipped clients:');
            $this->table(['Folder', 'Reason'], $this->clientsSkipped);
        }

        return 0;
    }

    private function processClientFolder(SplFileInfo $folder, bool $dry): void {
        $name = $folder->getFilename();

        // Expected format: "1142 SMITH, JOHN" — strip leading number, then parse "SURNAME, FORENAME"
        if (!preg_match('/\A\d+\s+(.+)/u', $name, $m)) {
            $this->warn("    Skipping [{$name}] — unexpected folder name format.");
            $this->clientsSkipped[] = [$name, 'Unexpected folder name format'];
            return;
        }

        $folderName = trim($m[1]); // e.g. "SMITH, JOHN"

        if (!str_contains($folderName, ',')) {
            $this->warn("    Skipping [{$name}] — cannot parse SURNAME, FORENAME.");
            $this->clientsSkipped[] = [$name, 'Cannot parse SURNAME, FORENAME'];
            return;
        }

        [$folderSurname, $folderForename] = array_map('trim', explode(',', $folderName, 2));

        $client = Client::whereRaw('UPPER(name_last) = ?', [strtoupper($folderSurname)])
            ->whereRaw('UPPER(name_first) = ?', [strtoupper($folderForename)])
            ->first();

        if (!$client) {
            $this->warn("    Skipping [{$name}] — no client found matching [{$folderSurname}, {$folderForename}].");
            $this->clientsSkipped[] = [$name, "No client matched [{$folderSurname}, {$folderForename}]"];
            return;
        }

        $this->clientsFound++;
        $this->line("    Client [{$name}] → ID {$client->id} ({$client->fullName})");

        foreach (File::directories($folder->getRealPath()) as $typePath) {
            $this->processDocTypeFolder(new SplFileInfo($typePath), $client, $dry);
        }
    }

    private function processDocTypeFolder(SplFileInfo $typeFolder, Client $client, bool $dry): void {
        $folderName = $typeFolder->getFilename();

        if (!preg_match('/\A\d+\s+(.+)/u', $folderName, $m)) {
            $this->warn("      Skipping type folder [{$folderName}] — unexpected name format.");
            return;
        }

        $typeName = trim($m[1]);
        $this->line("      Doc type: [{$typeName}]");

        if ($dry) {
            $docType = DocumentType::where('display_name', $typeName)->first();
            if (!$docType) {
                $this->comment("        Would create DocumentType [{$typeName}]");
            }
        } else {
            $docType = DocumentType::firstOrCreate(['display_name' => $typeName]);
        }

        foreach (File::files($typeFolder->getRealPath()) as $file) {
            if (str_starts_with($file->getFilename(), '.')) {
                continue;
            }
            $this->processFile($file, $client, $typeName, $docType ?? null, $dry);
        }
    }

    private function processFile(
        SplFileInfo  $file,
        Client       $client,
        string       $typeName,
        ?DocumentType $docType,
        bool         $dry
    ): void {
        $filename = $file->getFilename();
        $this->line("        " . ($dry ? '[DRY] ' : '') . "Importing [{$filename}]");

        if ($dry) {
            $this->filesImported++;
            return;
        }

        try {
            $storagePath = $client->getDocumentStoragePath();

            Storage::putFileAs($storagePath, $file, $filename);

            $fullPath = Storage::path($storagePath . $filename);

            $client->documents()->create([
                'solicitor_id' => null,
                'user_id'      => 1,
                'display_name' => $docType->display_name,
                'file_name'    => $filename,
                'file_type'    => File::extension($fullPath),
                'file_size'    => filesize($fullPath),
                'type_id'      => $docType->id,
            ]);

            $this->filesImported++;
        } catch (\Throwable $e) {
            $this->error("        Failed [{$filename}]: " . $e->getMessage());
        }
    }
}
