<?php

namespace App\Http\Controllers\Admin;

use App\Http\Controllers\Controller;
use App\Models\Contract;
use App\Traits\LogsAdminActivity;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;

class ContractController extends Controller
{
    use LogsAdminActivity;

    /**
     * Display a listing of contracts.
     */
    public function index()
    {
        return view('admin.contracts.index');
    }

    /**
     * Show the form for creating a new contract.
     */
    public function create()
    {
        return view('admin.contracts.create');
    }

    /**
     * Store a newly created contract.
     */
    public function store(Request $request)
    {
        $validated = $request->validate([
            'title' => 'required|string|max:255',
            'content' => 'required|string',
            'slug' => [
                'nullable', 'string', 'max:255',
                'regex:/^[a-z0-9]+(?:-[a-z0-9]+)*$/',
                function ($attribute, $value, $fail) {
                    if (! $value) return;
                    if (Contract::where('slug', $value)->exists()) {
                        $fail(__('admin/contracts.errors_slug_taken'));
                    }
                },
            ],
            'type' => ['required', \Illuminate\Validation\Rule::in(array_map(fn ($c) => $c->value, \App\Enums\ContractType::cases()))],
            'status' => 'required|in:active,draft,archived',
            'version' => 'nullable|string|max:50',
            'requires_acceptance' => 'nullable|boolean',
            'sort_order' => 'nullable|integer',
            'effective_date' => 'nullable|date',
            'grace_until' => ['nullable', 'date'],
            'translations.*.slug' => [
                'nullable', 'string', 'max:255',
                'regex:/^[a-z0-9]+(?:-[a-z0-9]+)*$/',
            ],
        ]);

        try {
            DB::beginTransaction();

            $validated['version'] = $validated['version'] ?? '1.0';
            $validated['sort_order'] = $validated['sort_order'] ?? 0;
            $validated['requires_acceptance'] = $request->has('requires_acceptance') ? 1 : 0;
            // Empty slug → unset so the model's boot() auto-derives from title
            if (empty($validated['slug'])) {
                unset($validated['slug']);
            }

            $contract = Contract::create($validated);

            // Persist per-locale translations posted by translation-tabbed-group.
            // Title goes first so the model's setTranslation override can
            // auto-derive a per-locale slug if the admin left the slug field empty.
            if (is_array($request->input('translations'))) {
                $payload = $request->input('translations');
                foreach ($payload as $locale => $fields) {
                    if (! is_array($fields)) {
                        continue;
                    }
                    // Pass 1: title (so slug auto-derive can run).
                    if (array_key_exists('title', $fields)) {
                        $contract->setTranslation('title', $locale, (string) ($fields['title'] ?? ''));
                    }
                    // Pass 2: explicit slug (overrides any auto-derived value).
                    if (array_key_exists('slug', $fields) && $fields['slug'] !== null && $fields['slug'] !== '') {
                        $contract->setTranslation('slug', $locale, \Illuminate\Support\Str::slug((string) $fields['slug']));
                    }
                    // Pass 3: remaining whitelisted fields.
                    foreach ($fields as $field => $value) {
                        if (! in_array($field, ['content'], true)) {
                            continue;
                        }
                        $contract->setTranslation($field, $locale, (string) ($value ?? ''));
                    }
                }
            }

            $this->logCreated($contract, "Created contract #{$contract->id} - {$contract->title}");

            DB::commit();

            $this->dispatchNewVersionEmail($contract);

            return redirect()->route('admin.contracts.index')
                ->with('success', __('admin/contracts.messages_created'));
        } catch (\Exception $e) {
            DB::rollBack();

            return redirect()->back()
                ->withInput()
                ->with('error', __('admin/contracts.messages_create_error'));
        }
    }

    /**
     * Dispatch the new-version-available email to all clients who accepted any
     * previous version of the same contract type. No-op when:
     *   - the agreements_send_new_version_email setting is off,
     *   - the contract is not active (drafts/archived shouldn't notify),
     *   - there is no previous contract of the same type (this is the first
     *     version, not a "new version" announcement).
     *
     * Per-client dispatches are deduped via contract_reminder_logs so re-saving
     * the same contract row does not double-send.
     */
    protected function dispatchNewVersionEmail(Contract $contract): void
    {
        if (! \App\Models\Setting::getValue('agreements_send_new_version_email', '1')) {
            return;
        }
        if ($contract->status !== 'active') {
            return;
        }

        $previousIds = Contract::where('type', $contract->type)
            ->where('id', '!=', $contract->id)
            ->pluck('id');

        if ($previousIds->isEmpty()) {
            return; // No previous version → not a "new version" announcement
        }

        $clientIds = \App\Models\ClientContractAcceptance::whereIn('contract_id', $previousIds)
            ->distinct()->pluck('client_id');

        foreach ($clientIds as $clientId) {
            $alreadySent = \App\Models\ContractReminderLog::where('contract_id', $contract->id)
                ->where('client_id', $clientId)
                ->where('kind', 'new_version')
                ->whereNull('days_before_cutoff')
                ->exists();
            if ($alreadySent) {
                continue;
            }

            \App\Jobs\SendEmailJob::dispatch(
                'Contract New Version Available',
                'client',
                (int) $clientId,
                [
                    'CONTRACT_TITLE' => $contract->getTranslation('title') ?: (string) $contract->title,
                    'CONTRACT_VERSION' => (string) $contract->version,
                    'GRACE_UNTIL_DATE' => $contract->grace_until?->format('Y-m-d') ?? __('store.contracts_immediate_cutoff'),
                    'CONTRACT_URL' => route('legal.contract.show', $contract->slug),
                ]
            );

            \App\Models\ContractReminderLog::create([
                'contract_id' => $contract->id,
                'client_id' => $clientId,
                'kind' => 'new_version',
                'days_before_cutoff' => null,
                'sent_at' => now(),
            ]);
        }
    }

    /**
     * Show the form for editing the specified contract.
     */
    public function edit(Contract $contract)
    {
        return view('admin.contracts.edit', compact('contract'));
    }

    /**
     * Update the specified contract.
     */
    public function update(Request $request, Contract $contract)
    {
        $validated = $request->validate([
            'title' => 'required|string|max:255',
            'content' => 'required|string',
            'slug' => [
                'nullable', 'string', 'max:255',
                'regex:/^[a-z0-9]+(?:-[a-z0-9]+)*$/',
                function ($attribute, $value, $fail) use ($contract) {
                    if (! $value) return;
                    if (Contract::where('slug', $value)->where('id', '!=', $contract->id)->exists()) {
                        $fail(__('admin/contracts.errors_slug_taken'));
                    }
                },
            ],
            'type' => ['required', \Illuminate\Validation\Rule::in(array_map(fn ($c) => $c->value, \App\Enums\ContractType::cases()))],
            'status' => 'required|in:active,draft,archived',
            'version' => 'nullable|string|max:50',
            'requires_acceptance' => 'nullable|boolean',
            'sort_order' => 'nullable|integer',
            'effective_date' => 'nullable|date',
            'grace_until' => ['nullable', 'date'],
            'translations.*.slug' => [
                'nullable', 'string', 'max:255',
                'regex:/^[a-z0-9]+(?:-[a-z0-9]+)*$/',
            ],
        ]);

        try {
            DB::beginTransaction();

            $validated['version'] = $validated['version'] ?? '1.0';
            $validated['sort_order'] = $validated['sort_order'] ?? 0;
            $validated['requires_acceptance'] = $request->has('requires_acceptance') ? 1 : 0;
            // Empty slug → unset so updating()'s auto-derive handles it
            if (array_key_exists('slug', $validated) && empty($validated['slug'])) {
                unset($validated['slug']);
            }

            $contract->update($validated);

            // Persist per-locale translations posted by translation-tabbed-group.
            if (is_array($request->input('translations'))) {
                $payload = $request->input('translations');
                foreach ($payload as $locale => $fields) {
                    if (! is_array($fields)) {
                        continue;
                    }
                    if (array_key_exists('title', $fields)) {
                        $contract->setTranslation('title', $locale, (string) ($fields['title'] ?? ''));
                    }
                    if (array_key_exists('slug', $fields) && $fields['slug'] !== null && $fields['slug'] !== '') {
                        $contract->setTranslation('slug', $locale, \Illuminate\Support\Str::slug((string) $fields['slug']));
                    }
                    foreach ($fields as $field => $value) {
                        if (! in_array($field, ['content'], true)) {
                            continue;
                        }
                        $contract->setTranslation($field, $locale, (string) ($value ?? ''));
                    }
                }
            }

            $this->logUpdated($contract, "Updated contract #{$contract->id} - {$contract->title}");

            DB::commit();

            return redirect()->route('admin.contracts.index')
                ->with('success', __('admin/contracts.messages_updated'));
        } catch (\Exception $e) {
            DB::rollBack();

            return redirect()->back()
                ->withInput()
                ->with('error', __('admin/contracts.messages_update_error'));
        }
    }

    /**
     * Remove the specified contract.
     */
    public function destroy(Contract $contract)
    {
        try {
            DB::beginTransaction();

            $this->logDeleted($contract, "Deleted contract #{$contract->id} - {$contract->title}");

            $contract->delete();

            DB::commit();

            return response()->json([
                'success' => true,
                'message' => __('admin/contracts.messages_deleted'),
            ]);
        } catch (\Exception $e) {
            DB::rollBack();

            return response()->json([
                'success' => false,
                'message' => __('admin/contracts.messages_delete_error'),
            ], 500);
        }
    }

    /**
     * DataTables records endpoint.
     */
    public function records(Request $request): JsonResponse
    {
        $query = Contract::query();

        // Status filter
        if ($request->filled('status')) {
            $query->where('status', $request->input('status'));
        }

        // Type filter
        if ($request->filled('type')) {
            $query->where('type', $request->input('type'));
        }

        // Ordering
        $query->orderBy('sort_order', 'asc')->orderBy('created_at', 'desc');

        $perPage = $request->input('length', -1);

        if ($perPage == -1) {
            $results = $query->get();
            $total = $results->count();

            $paginatedResults = new \stdClass;
            $paginatedResults->data = $results;
            $paginatedResults->total = $total;
            $results = $paginatedResults;
        } else {
            $page = ($request->input('start', 0) / $perPage) + 1;
            $results = $query->paginate($perPage, ['*'], 'page', $page);
        }

        $dataCollection = $perPage == -1 ? $results->data : $results;
        $data = $dataCollection->map(function (Contract $contract) {
            return [
                'id' => $contract->id,
                'title' => $contract->title,
                'type' => $contract->type,
                'status' => $contract->status,
                'version' => $contract->version,
                'requires_acceptance' => $contract->requires_acceptance,
                'effective_date' => $contract->effective_date ? $contract->effective_date->format('d.m.Y') : '-',
                'created_at' => $contract->created_at->format('d.m.Y H:i'),
            ];
        });

        $total = $perPage == -1 ? $results->total : $results->total();

        return response()->json([
            'draw' => (int) $request->input('draw'),
            'recordsTotal' => $total,
            'recordsFiltered' => $total,
            'data' => $data,
        ]);
    }
}
