<?php

namespace App\Http\Controllers\Admin;

use App\Http\Controllers\Controller;
use App\Models\Business;
use App\Models\ContractTemplate;
use App\Services\AuditService;
use App\Services\DocxContractTemplateService;
use App\Services\PdfContractTemplateService;
use App\Services\SecureFileService;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Storage;
use Illuminate\Validation\Rule;
use Illuminate\View\View;
use Symfony\Component\HttpFoundation\BinaryFileResponse;
use Symfony\Component\HttpFoundation\StreamedResponse;

class ContractTemplateController extends Controller
{
    public function index(): View
    {
        $business=Business::firstOrFail();
        $templates=ContractTemplate::query()->where('business_id',$business->id)->with('uploader')->orderByDesc('is_default')->latest('id')->get();
        return view('admin.contract-templates.index',compact('business','templates'));
    }

    public function create(DocxContractTemplateService $docx): View
    {
        return view('admin.contract-templates.create',['signatories'=>ContractTemplate::SIGNATORIES,'fieldCount'=>count($docx->fieldCatalog())]);
    }

    public function store(Request $request, SecureFileService $files, DocxContractTemplateService $docx, PdfContractTemplateService $pdf): RedirectResponse
    {
        $business=Business::firstOrFail();
        $data=$request->validate([
            'name'=>['required','string','max:255'],'template_file'=>['required','file','mimes:docx,pdf','max:15360'],
            'required_signatories'=>['nullable','array'],'required_signatories.*'=>['string',Rule::in(array_keys(ContractTemplate::SIGNATORIES))],
            'make_default'=>['nullable','boolean'],
        ]);
        $file=$data['template_file'];
        $format=strtolower((string)$file->getClientOriginalExtension());
        abort_unless(in_array($format,ContractTemplate::FORMATS,true),422,'Only DOCX and PDF contract templates are supported.');
        $path=$files->store($file,'contract-templates/'.$business->id);
        try {
            $inspection=$format==='pdf'
                ? $pdf->inspectPath(Storage::disk('local')->path($path))
                : $docx->inspectPath(Storage::disk('local')->path($path));
        } catch (\Throwable $e) {
            $files->delete($path);
            throw $e;
        }

        $mappings=$format==='docx'
            ? collect($inspection['mappings'])->map(function(array $mapping): array {
                $mapping['spaces_before']=(int)($mapping['spaces_before'] ?? (($mapping['mode'] ?? '')==='append'?1:0));
                $mapping['spaces_after']=(int)($mapping['spaces_after'] ?? 0);
                $mapping['trim_surrounding']=(bool)($mapping['trim_surrounding'] ?? false);
                return $mapping;
            })->all()
            : [];

        $template=DB::transaction(function() use($request,$business,$file,$format,$path,$inspection,$mappings,$data): ContractTemplate {
            $makeDefault=$request->boolean('make_default');
            if ($makeDefault) ContractTemplate::where('business_id',$business->id)->update(['is_default'=>false]);
            return ContractTemplate::create([
                'business_id'=>$business->id,'name'=>$data['name'],'version'=>1,'original_name'=>$file->getClientOriginalName(),'format'=>$format,'file_path'=>$path,
                'extracted_text'=>$inspection['extracted_text'],'blocks'=>$inspection['blocks'],'mappings'=>$mappings,
                'required_signatories'=>$data['required_signatories'] ?? ['borrower','guarantor','loan_officer','office_stamp'],
                'status'=>$makeDefault?'active':'draft','is_default'=>$makeDefault,'uploaded_by'=>$request->user()->id,'updated_by'=>$request->user()->id,
            ]);
        });
        AuditService::record('contract_template.uploaded',$template,[],[
            'name'=>$template->name,'original_name'=>$template->original_name,'format'=>$format,'auto_mappings'=>count($template->mappings ?? []),'is_default'=>$template->is_default,
        ]);
        $message=$format==='pdf'
            ? 'PDF contract uploaded. Add the system fields you want to overlay and position them on the correct pages.'
            : 'Word contract uploaded and extracted. Review the detected fields and spacing before using it.';
        return redirect()->route('admin.contract-templates.edit',$template)->with('success',$message);
    }

    public function edit(ContractTemplate $contractTemplate, DocxContractTemplateService $docx): View
    {
        $this->assertBusiness($contractTemplate);
        return view('admin.contract-templates.edit',[
            'template'=>$contractTemplate,'fields'=>$docx->fieldCatalog(),'modes'=>DocxContractTemplateService::MODES,
            'pdfAligns'=>PdfContractTemplateService::ALIGNS,'signatories'=>ContractTemplate::SIGNATORIES,
        ]);
    }

    public function update(Request $request, ContractTemplate $contractTemplate, DocxContractTemplateService $docx): RedirectResponse
    {
        $this->assertBusiness($contractTemplate);
        $fields=array_keys($docx->fieldCatalog());
        $common=[
            'name'=>['required','string','max:255'],'status'=>['required',Rule::in(ContractTemplate::STATUSES)],'make_default'=>['nullable','boolean'],
            'required_signatories'=>['nullable','array'],'required_signatories.*'=>['string',Rule::in(array_keys(ContractTemplate::SIGNATORIES))],
            'mappings'=>['nullable','array'],'mappings.*.field'=>['nullable','string',Rule::in($fields)],'mappings.*.label'=>['nullable','string','max:255'],
        ];
        $rules=$contractTemplate->isPdf() ? array_merge($common,[
            'mappings.*.page'=>['nullable','integer','min:1','max:1000'],'mappings.*.x'=>['nullable','numeric','between:0,2000'],
            'mappings.*.y'=>['nullable','numeric','between:0,2000'],'mappings.*.width'=>['nullable','numeric','between:5,2000'],
            'mappings.*.font_size'=>['nullable','numeric','between:6,30'],'mappings.*.line_height'=>['nullable','numeric','between:3,20'],
            'mappings.*.align'=>['nullable','string',Rule::in(array_keys(PdfContractTemplateService::ALIGNS))],
        ]) : array_merge($common,[
            'mappings.*.block_id'=>['nullable','string','max:40'],'mappings.*.segment_index'=>['nullable','integer','min:-1','max:5000'],
            'mappings.*.mode'=>['nullable','string',Rule::in(array_keys(DocxContractTemplateService::MODES))],
            'mappings.*.spaces_before'=>['nullable','integer','between:0,10'],'mappings.*.spaces_after'=>['nullable','integer','between:0,10'],
            'mappings.*.trim_surrounding'=>['nullable','boolean'],
        ]);
        $data=$request->validate($rules);

        if ($contractTemplate->isPdf()) {
            $pageCount=max(1,count($contractTemplate->blocks ?? []));
            $mappings=collect($data['mappings'] ?? [])->filter(fn($row)=>filled($row['field'] ?? null))->map(function($row) use($pageCount): array {
                $page=max(1,min($pageCount,(int)($row['page'] ?? 1)));
                return [
                    'block_id'=>'pdf:'.$page,'segment_index'=>-1,'field'=>(string)$row['field'],'mode'=>'overlay','label'=>(string)($row['label'] ?? 'PDF overlay'),
                    'page'=>$page,'x'=>(float)($row['x'] ?? 20),'y'=>(float)($row['y'] ?? 20),'width'=>(float)($row['width'] ?? 80),
                    'font_size'=>(float)($row['font_size'] ?? 10),'line_height'=>(float)($row['line_height'] ?? 5),'align'=>(string)($row['align'] ?? 'L'),
                ];
            })->values()->all();
        } else {
            $mappings=collect($data['mappings'] ?? [])->filter(fn($row)=>filled($row['block_id'] ?? null) && filled($row['field'] ?? null))->map(function($row): array {
                $mode=(string)($row['mode'] ?? 'replace_blank');
                return [
                    'block_id'=>(string)$row['block_id'],'segment_index'=>(int)($row['segment_index'] ?? -1),'field'=>(string)$row['field'],'mode'=>$mode,
                    'label'=>(string)($row['label'] ?? ''),'spaces_before'=>(int)($row['spaces_before'] ?? ($mode==='append'?1:0)),
                    'spaces_after'=>(int)($row['spaces_after'] ?? 0),'trim_surrounding'=>filter_var($row['trim_surrounding'] ?? false,FILTER_VALIDATE_BOOLEAN),
                ];
            })->values()->all();
        }

        $before=$contractTemplate->toArray();
        DB::transaction(function() use($request,$contractTemplate,$data,$mappings): void {
            $makeDefault=$request->boolean('make_default');
            if ($makeDefault) ContractTemplate::where('business_id',$contractTemplate->business_id)->whereKeyNot($contractTemplate->id)->update(['is_default'=>false]);
            $contractTemplate->update([
                'name'=>$data['name'],'status'=>$makeDefault?'active':$data['status'],'is_default'=>$makeDefault,
                'required_signatories'=>$data['required_signatories'] ?? [],'mappings'=>$mappings,'updated_by'=>$request->user()->id,
            ]);
        });
        AuditService::record('contract_template.updated',$contractTemplate,$before,$contractTemplate->fresh()->toArray());
        return back()->with('success',$contractTemplate->isPdf()?'PDF overlay fields and positions saved.':'Contract template mappings and spacing defaults saved.');
    }

    public function makeDefault(Request $request, ContractTemplate $contractTemplate): RedirectResponse
    {
        $this->assertBusiness($contractTemplate);
        $before=$contractTemplate->toArray();
        DB::transaction(function() use($request,$contractTemplate): void {
            ContractTemplate::where('business_id',$contractTemplate->business_id)->update(['is_default'=>false]);
            $contractTemplate->update(['is_default'=>true,'status'=>'active','updated_by'=>$request->user()->id]);
        });
        AuditService::record('contract_template.default_changed',$contractTemplate,$before,$contractTemplate->fresh()->toArray());
        return back()->with('success','Default loan contract template updated.');
    }

    public function download(ContractTemplate $contractTemplate, SecureFileService $files): StreamedResponse
    {
        $this->assertBusiness($contractTemplate);
        return $files->download($contractTemplate->file_path,$contractTemplate->original_name);
    }

    public function previewSource(ContractTemplate $contractTemplate): BinaryFileResponse
    {
        $this->assertBusiness($contractTemplate);
        abort_unless($contractTemplate->isPdf(),404);
        $path=Storage::disk('local')->path($contractTemplate->file_path);
        abort_unless(is_file($path),404,'The source PDF template is missing.');
        return response()->file($path,[
            'Content-Type'=>'application/pdf','Content-Disposition'=>'inline; filename="'.str_replace('"','',$contractTemplate->original_name).'"',
            'Cache-Control'=>'private, no-store, max-age=0',
        ]);
    }

    private function assertBusiness(ContractTemplate $template): void
    {
        abort_unless($template->business_id===Business::firstOrFail()->id,404);
    }
}
