<?php

namespace App\Services\Kyc;

use App\Models\UserDocument;
use App\Support\Audit;
use Illuminate\Http\UploadedFile;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Storage;
use Throwable;

/**
 * KYC document upload + verification — faithful Laravel port of kyc.php.
 * Files stored on the configured disk; doc_number PII-encrypted via the model.
 * Reviews audited into the shared audit_log.
 */
class KycService
{
    /**
     * KYC documents a user CATEGORY must / may provide (from the doc_types
     * master's multiselect `mandatory_for` / `optional_for` columns, each a CSV
     * of user-category codes). Returns:
     *   ['mandatory' => [...docs], 'optional' => [...docs]]
     * A document listed as mandatory takes precedence over optional.
     */
    public function docsForCategory(string $category): array
    {
        $out = ['mandatory' => [], 'optional' => []];
        $category = trim($category);
        if ($category === '') {
            return $out;
        }
        try {
            $hasMand = $this->hasCol('doc_types', 'mandatory_for');
            $hasOpt  = $this->hasCol('doc_types', 'optional_for');
            if (!$hasMand && !$hasOpt) {
                return $out;
            }
            $rows = DB::table('doc_types')
                ->where('is_active', 1)
                ->when($this->hasCol('doc_types', 'deleted_at'), fn ($q) => $q->whereNull('deleted_at'))
                ->orderBy('name')
                ->get();
            foreach ($rows as $r) {
                $doc = [
                    'code'       => $r->code ?? null,
                    'name'       => $r->name ?? null,
                    'file_types' => $r->file_types ?? null,
                ];
                $mand = $hasMand ? $this->csvList($r->mandatory_for ?? '') : [];
                $opt  = $hasOpt ? $this->csvList($r->optional_for ?? '') : [];
                if (in_array($category, $mand, true)) {
                    $out['mandatory'][] = $doc;
                } elseif (in_array($category, $opt, true)) {
                    $out['optional'][] = $doc;
                }
            }
        } catch (Throwable) {
            // fall through with whatever was gathered
        }
        return $out;
    }

    /**
     * Required document types for a category/role. Prefers the new
     * `mandatory_for` multiselect; falls back to the legacy `required_for`
     * column on older tables. Returns the mandatory doc list only.
     */
    public function requiredTypes(string $role): array
    {
        if ($this->hasCol('doc_types', 'mandatory_for')) {
            return $this->docsForCategory($role)['mandatory'];
        }
        // Legacy fallback (pre-multiselect tables still carrying required_for).
        try {
            return DB::table('doc_types')
                ->where('is_active', 1)
                ->when($this->hasCol('doc_types', 'deleted_at'), fn ($q) => $q->whereNull('deleted_at'))
                ->when($this->hasCol('doc_types', 'required_for'), fn ($q) => $q->where(function ($w) use ($role) {
                    $w->where('required_for', $role)
                        ->orWhere('required_for', 'both')
                        ->orWhereNull('required_for');
                }))
                ->orderBy('name')
                ->get()
                ->map(fn ($r) => (array) $r)
                ->all();
        } catch (Throwable) {
            return [];
        }
    }

    /** Split a CSV string into a trimmed, non-empty list. */
    private function csvList($v): array
    {
        return array_values(array_filter(array_map('trim', explode(',', (string) $v))));
    }

    /** Upload a KYC document for a user. Returns the created row (doc_number decrypted). */
    public function upload(int $userId, string $role, string $docType, string $docNumber, UploadedFile $file): array
    {
        $disk = config('kyc.disk', 'public');
        $path = $file->store(config('kyc.folder', 'kyc'), $disk);
        $fileUrl = Storage::disk($disk)->url($path);

        $doc = new UserDocument();
        $doc->user_id = $userId;
        $doc->role = $role;
        $doc->doc_type = $docType;
        $doc->doc_number = $docNumber !== '' ? $docNumber : null; // encrypted by the model
        $doc->file_url = $fileUrl;
        $doc->status = 'pending';
        $doc->save();

        Audit::log('kyc', $doc->id, 'upload', null, ['user' => $userId, 'type' => $docType]);

        return $this->present($doc->fresh());
    }

    /** A user's own documents (doc_number decrypted for the owner). */
    public function forUser(int $userId): array
    {
        return UserDocument::query()
            ->where('user_id', $userId)
            ->orderByDesc('id')
            ->get()
            ->map(fn ($d) => $this->present($d))
            ->all();
    }

    /** Admin review queue (optionally filtered by status/role), with user names. */
    public function queue(?string $status = 'pending', ?string $role = null): array
    {
        return UserDocument::query()
            ->when($status, fn ($q) => $q->where('status', $status))
            ->when($role, fn ($q) => $q->where('role', $role))
            ->orderByDesc('id')
            ->limit(200)
            ->get()
            ->map(function ($d) {
                $row = $this->present($d);
                $u = DB::table('users')->where('id', $d->user_id)->first(['name', 'email', 'phone']);
                $row['user_name'] = $u->name ?? null;
                $row['user_email'] = $u->email ?? null;
                $row['user_phone'] = $u->phone ?? null;
                return $row;
            })
            ->all();
    }

    /** Verify / reject / reset a document. */
    public function review(int $id, string $decision, string $notes = ''): bool
    {
        $decision = in_array($decision, config('kyc.statuses', ['pending', 'verified', 'rejected']), true) ? $decision : 'pending';
        $doc = UserDocument::find($id);
        if (!$doc) {
            return false;
        }
        $doc->status = $decision;
        $doc->notes = $notes !== '' ? $notes : null;
        $doc->reviewed_by = auth()->id();
        $doc->reviewed_at = now();
        $doc->save();

        Audit::log('kyc', $id, $decision, null, ['notes' => $notes]);
        return true;
    }

    /**
     * Overall KYC status for a user: 'verified' only if ≥1 doc and none pending/rejected;
     * 'pending' / 'rejected' short-circuit; 'none' if no documents.
     */
    public function statusForUser(int $userId): string
    {
        $statuses = UserDocument::query()->where('user_id', $userId)->pluck('status');
        if ($statuses->isEmpty()) {
            return 'none';
        }
        $hasVerified = false;
        foreach ($statuses as $s) {
            if ($s === 'verified') {
                $hasVerified = true;
            }
            if ($s === 'pending') {
                return 'pending';
            }
            if ($s === 'rejected') {
                return 'rejected';
            }
        }
        return $hasVerified ? 'verified' : 'pending';
    }

    /** Shape a document row for the API (doc_number already decrypted by the model). */
    private function present(UserDocument $d): array
    {
        return [
            'id'          => $d->id,
            'user_id'     => $d->user_id,
            'role'        => $d->role,
            'doc_type'    => $d->doc_type,
            'doc_number'  => $d->doc_number,
            'file_url'    => $d->file_url,
            'status'      => $d->status,
            'notes'       => $d->notes,
            'reviewed_by' => $d->reviewed_by,
            'reviewed_at' => $d->reviewed_at,
            'created_at'  => $d->created_at,
        ];
    }

    private function hasCol(string $table, string $col): bool
    {
        try {
            return in_array($col, \Illuminate\Support\Facades\Schema::getColumnListing($table), true);
        } catch (Throwable) {
            return false;
        }
    }
}
