<?php

namespace App\Console\Commands;

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

class ImportDocuments extends Command {
    /**
     * Define the "Golden List" of valid document types.
     * The key is the standardized name, and the value is an array of keywords to match.
     */
    protected const VALID_TYPES = [
        'CFA' => ['cfa', 'conditional fee', 'mma'],
        'ID' => ['id', 'identification', 'passport', 'license'],
        'POA' => ['poa', 'proof of address', 'utility bill', 'council tax bill'],
        'Statement' => ['statement', 'witness statement'],
        'Claim Form' => ['claim form', 'form', 'cf'],
        'Medical DsarReport' => ['medical', 'report', 'clinic'],
        'Part 1' => ['part 1'],
        'Part 3' => ['part 3'],
        'Application Receipt' => ['receipt', 'application letter','assessment letter', 'first assessment'],
    ];

    /**
     * The name and signature of the console command.
     *
     * @var string
     */
    protected $signature = 'documents:import 
                            {path : The absolute path to the root directory containing client folders}';

    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = 'Loop through Claims, find matching folders by case number, and upload documents.';

    /**
     * Execute the console command.
     */
    public function handle() {
        $rootPath = $this->argument('path');

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

        $this->info("Scanning [{$rootPath}] and matching against Claims...");

        // 2. Loop over Claims
        Claim::chunk(50, function ($claims) use ($rootPath)
        {
            foreach ($claims as $claim) {
                $caseNumber = $claim->case_number;

                if (empty($caseNumber)) {
                    continue;
                }

                $this->comment("Processing Claim: {$caseNumber} (ID: {$claim->id})");

                // 3. Find the matching subfolder
                $targetFolder = $this->findSubfolder($rootPath, $caseNumber);

                if (!$targetFolder) {
                    continue;
                }

                $this->info("  - Found folder: " . $targetFolder->getFilename());

                // 4. Process files in the found folder
                $this->processFolderFiles($targetFolder, $claim);
            }
        });

        $this->newLine();
        $this->info("Done processing claims.");
        return 0;
    }

    /**
     * Helper to find a subfolder matching the search string.
     */
    private function findSubfolder(string $rootPath, string $search): ?SplFileInfo {
        $directories = File::directories($rootPath);

        foreach ($directories as $dirPath) {
            $dir = new SplFileInfo($dirPath);

            if (Str::contains(strtolower($dir->getFilename()), strtolower($search))) {
                return $dir;
            }
        }

        return null;
    }

    /**
     * Loop through files in the folder and process them.
     */
    private function processFolderFiles(SplFileInfo $folder, Claim $claim) {
        $files = File::files($folder->getRealPath());

        foreach ($files as $file) {
            if (Str::startsWith($file->getFilename(), '.')) {
                continue;
            }

            $this->processFile($file, $claim);
        }
    }

    /**
     * Process a single file: Extract type and simulate upload.
     */
    private function processFile(SplFileInfo $file, Claim $claim) {
        $filename = $file->getFilename();

        $stringsToRemove = [
            $claim->case_number,
            $claim->client_name ?? '', // Remove client name if available
        ];

        // 1. Clean the filename to get a "raw" type string
        $rawType = $this->guessDocumentType($file->getFilenameWithoutExtension(), $stringsToRemove);

        // 2. Match the raw string against our "Best Match" list
        $bestType = $this->findBestTypeMatch($rawType);

        $this->line("    + Uploading [{$filename}] matched as: <info>{$bestType}</info> (extracted: {$rawType})");

        // --- UPLOAD LOGIC ---
        try {
            if (array_key_exists($bestType, self::VALID_TYPES)) {
                $path = Storage::putFileAs(
                    $claim->client->getDocumentStoragePath(),
                    $file,
                    $filename
                );

                $docType = DocumentType::where('display_name', $bestType)->first();

                $claim->client->documents()->create([
                    'solicitor_id' => $claim->solicitor_id,
                    'user_id' => 1,
                    'display_name' => $docType->display_name,
                    'file_name' => $filename,
                    'file_type' => File::extension(Storage::path($claim->client->getDocumentStoragePath() . $filename)),
                    'file_size' => filesize(Storage::path($claim->client->getDocumentStoragePath() . $filename)),
                    'type_id' => $docType->id,
                ]);
            }

        } catch (\Exception $e) {
            $this->error("    ! Failed to upload: " . $e->getMessage());
        }
    }

    /**
     * Heuristic to extract document type from filename.
     */
    private function guessDocumentType(string $name, array $contextStrings): string {
        if (strlen($name) <= 5) {
            return strtoupper($name);
        }

        $cleanName = $name;

        foreach ($contextStrings as $str) {
            if (!empty($str)) {
                $cleanName = str_ireplace($str, '', $cleanName);
            }
        }

        $cleanName = preg_replace('/^[\s\-_]+/', '', $cleanName);
        $cleanName = preg_replace('/\b\d{6,8}\b/', '', $cleanName);
        $cleanName = trim($cleanName);
        $cleanName = ltrim($cleanName, '-_');
        $cleanName = trim($cleanName);

        return empty($cleanName) ? $name : $cleanName;
    }

    /**
     * Compares the cleaned string against the defined list of valid types.
     */
    private function findBestTypeMatch(string $rawType): string {
        $lowerRaw = strtolower($rawType);

        foreach (self::VALID_TYPES as $standardName => $keywords) {
            foreach ($keywords as $keyword) {
                if (Str::contains($lowerRaw, strtolower($keyword))) {
                    return $standardName;
                }
            }
        }

        // Return capitalized raw type if no match found in the list
        return Str::title($rawType);
    }
}