<?php

namespace App\Http\Controllers\Agency\Operation;

use App\Http\Controllers\Controller;
use App\Models\Operation\OperationExpense;
use App\Models\Payment\Payment;
use App\Models\ResourceSchedule;
use App\Models\Supplier\Supplier;
use App\Models\User;
use App\Services\Operation\OperationService;
use App\Services\Payment\PaymentPostingService;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;

/**
 * Operasyon kartındaki "Giderler" sekmesinin yazma ucu.
 *
 * Planlanan gider kalemleri burada elle açılır/güncellenir; gerçekleşen ödeme
 * ise ödemeler defterine (payments) düşer ve payable ile kaleme bağlanır.
 * Ayrıntılı ödeme (dekont, yöntem, fatura eşleştirmesi) için karttaki
 * "Ödeme Kaydı" düğmesi mevcut quick-create akışını açar.
 */
class OperationExpenseController extends Controller
{
    public function __construct(
        private OperationService $ops,
        private PaymentPostingService $posting
    ) {
    }

    private function agencyId(): int
    {
        return Auth::user()->currentAgencyId();
    }

    private function resolveOrFail(string $type, int $id): Model
    {
        $date = $this->ops->resolveByKey($type, $id);
        abort_if(!$date, 404);
        $tour = $this->ops->tourOf($date);
        abort_if(!$tour || (int) $tour->agency_id !== $this->agencyId(), 403, 'Bu işlem için yetkiniz yok.');
        return $date;
    }

    /** Kalemin bu kalkışa ve bu acenteye ait olduğunu doğrular. */
    private function expenseOrFail(Model $date, int $expenseId): OperationExpense
    {
        $expense = OperationExpense::byAgency($this->agencyId())
            ->forDeparture(get_class($date), $date->getKey())
            ->find($expenseId);

        abort_if(!$expense, 404, 'Gider kalemi bulunamadı.');

        return $expense;
    }

    // ─── Planlanan gider kalemleri ───────────────────────────────

    public function store(Request $request, string $type, int $id)
    {
        $date = $this->resolveOrFail($type, $id);
        $data = $this->validatePayload($request);

        $expense = OperationExpense::create($data + [
            'agency_id' => $this->agencyId(),
            'operatable_type' => get_class($date),
            'operatable_id' => $date->getKey(),
            'status' => 'planned',
        ]);

        return response()->json([
            'success' => true,
            'message' => 'Gider kalemi eklendi.',
            'expense_id' => $expense->id,
        ]);
    }

    public function update(Request $request, string $type, int $id, int $expense)
    {
        $date = $this->resolveOrFail($type, $id);
        $row = $this->expenseOrFail($date, $expense);
        $data = $this->validatePayload($request);

        $row->update($data);

        return response()->json(['success' => true, 'message' => 'Gider kalemi güncellendi.']);
    }

    /** Tek alan güncelleme (tablo içinden hızlı düzenleme). */
    public function updateField(Request $request, string $type, int $id, int $expense)
    {
        $date = $this->resolveOrFail($type, $id);
        $row = $this->expenseOrFail($date, $expense);

        $data = $request->validate([
            'field' => 'required|string',
            'value' => 'nullable',
        ]);

        $allowed = ['title', 'description', 'planned_amount', 'due_date', 'category', 'status', 'notes'];
        if (!in_array($data['field'], $allowed, true)) {
            return response()->json(['success' => false, 'message' => 'Bu alan düzenlenemez.'], 422);
        }

        $value = $data['value'];

        if ($data['field'] === 'planned_amount') {
            $value = round((float) str_replace(',', '.', (string) $value), 2);
            if ($value < 0) {
                return response()->json(['success' => false, 'message' => 'Planlanan tutar negatif olamaz.'], 422);
            }
        }

        if ($data['field'] === 'category' && !array_key_exists($value, OperationExpense::CATEGORIES)) {
            return response()->json(['success' => false, 'message' => 'Geçersiz gider kategorisi.'], 422);
        }

        if ($data['field'] === 'status' && !array_key_exists($value, OperationExpense::STATUSES)) {
            return response()->json(['success' => false, 'message' => 'Geçersiz durum.'], 422);
        }

        if ($data['field'] === 'title' && trim((string) $value) === '') {
            return response()->json(['success' => false, 'message' => 'Kalem başlığı boş bırakılamaz.'], 422);
        }

        $row->update([$data['field'] => $value === '' ? null : $value]);

        return response()->json(['success' => true, 'message' => 'Güncellendi.']);
    }

    public function destroy(string $type, int $id, int $expense)
    {
        $date = $this->resolveOrFail($type, $id);
        $row = $this->expenseOrFail($date, $expense);

        // Kaleme bağlı ödeme varsa kayıt silinmez: gerçekleşen gider kaybolmasın
        if ($row->payments()->exists()) {
            return response()->json([
                'success' => false,
                'message' => 'Bu kaleme bağlı ödeme kaydı var. Önce ödemeyi silin ya da kalemi iptal edin.',
            ], 422);
        }

        $row->delete();

        return response()->json(['success' => true, 'message' => 'Gider kalemi silindi.']);
    }

    // ─── Gerçekleşen ödeme (hızlı giriş) ─────────────────────────

    /**
     * Kalem üzerinden gerçekleşen ödeme kaydı açar.
     *
     * Ödeme, ödemeler defterine normal bir gider hareketi olarak düşer;
     * payable ile kaleme (kaynak ataması / gider kalemi) ya da doğrudan
     * kalkışa bağlanır, böylece planlanan ↔ gerçekleşen eşleşir.
     */
    public function storePayment(Request $request, string $type, int $id)
    {
        $date = $this->resolveOrFail($type, $id);
        $agencyId = $this->agencyId();

        $data = $request->validate([
            'kind' => 'required|in:resource,manual,departure',
            'row_id' => 'nullable|integer',
            'amount' => 'required|numeric|min:0.01',
            'currency' => 'nullable|string|size:3',
            'method' => 'nullable|string',
            'status' => 'nullable|in:completed,pending',
            'paid_at' => 'nullable|date',
            'due_date' => 'nullable|date',
            'description' => 'nullable|string|max:255',
        ]);

        // nullable alanlar isteğe konmadıysa doğrulanmış diziye hiç girmez
        $method = $data['method'] ?? 'bank_transfer';
        if (!array_key_exists($method, Payment::selectableMethods())) {
            $method = 'bank_transfer';
        }

        $target = $this->resolveTarget($date, $agencyId, $data['kind'], $data['row_id'] ?? null);
        $status = $data['status'] ?? 'completed';
        $paidAt = $data['paid_at'] ?? null;
        $dueDate = $data['due_date'] ?? null;

        $payment = new Payment([
            'agency_id' => $agencyId,
            'direction' => 'out',
            'status' => $status,
            'method' => $method,
            'category' => $target['category'],
            'amount' => $data['amount'],
            'currency' => ($data['currency'] ?? null) ?: ($target['currency'] ?: 'TRY'),
            'exchange_rate' => 1,
            'paid_at' => $status === 'completed' ? ($paidAt ?: now()) : null,
            'due_date' => $status === 'pending' ? ($dueDate ?: now()->toDateString()) : $dueDate,
            'payable_type' => $target['payable_type'],
            'payable_id' => $target['payable_id'],
            'counterparty_type' => $target['counterparty_type'],
            'counterparty_id' => $target['counterparty_id'],
            'counterparty_name' => $target['counterparty_name'],
            'title' => $target['title'],
            'description' => ($data['description'] ?? null) ?: $target['description'],
        ]);
        $payment->save();

        $this->posting->post($payment);

        return response()->json([
            'success' => true,
            'message' => $status === 'pending' ? 'Planlanan ödeme kaydedildi.' : 'Ödeme kaydedildi.',
            'payment_id' => $payment->id,
            'quick_view_url' => route('agency.payments.quick-view', $payment),
        ]);
    }

    /** Ödemenin bağlanacağı hedefi (kalem / kaynak ataması / kalkış) çözer. */
    private function resolveTarget(Model $date, int $agencyId, string $kind, ?int $rowId): array
    {
        $tourName = $this->ops->tourOf($date)?->name ?? 'Kalkış';
        $dateLabel = $this->ops->startDateOf($date)?->format('d.m.Y') ?? '';
        $context = trim($tourName . ' · ' . $dateLabel, ' ·');

        if ($kind === 'manual' && $rowId) {
            $expense = $this->expenseOrFail($date, $rowId);

            return [
                'payable_type' => OperationExpense::class,
                'payable_id' => $expense->id,
                'category' => $expense->category,
                'currency' => $expense->currency,
                'counterparty_type' => $expense->counterparty_type,
                'counterparty_id' => $expense->counterparty_id,
                'counterparty_name' => $expense->counterparty_id ? null : $expense->counterparty_name,
                'title' => $expense->title . ' · ' . $context,
                'description' => $context,
            ];
        }

        if ($kind === 'resource' && $rowId) {
            $schedule = ResourceSchedule::byAgency($agencyId)
                ->forReference(get_class($date), $date->getKey())
                ->with('resource.supplier')
                ->find($rowId);

            abort_if(!$schedule, 404, 'Kaynak ataması bulunamadı.');

            $resource = $schedule->resource;
            $supplier = $resource?->supplier;

            return [
                'payable_type' => ResourceSchedule::class,
                'payable_id' => $schedule->id,
                'category' => $schedule->effective_role === 'guide' ? 'staff' : 'supplier',
                'currency' => $schedule->currency ?: 'TRY',
                'counterparty_type' => $supplier ? Supplier::class : null,
                'counterparty_id' => $supplier?->id,
                'counterparty_name' => $supplier ? null : ($resource->name ?? null),
                'title' => ($resource->name ?? 'Kaynak') . ' · ' . $context,
                'description' => $context,
            ];
        }

        // Kaleme bağlanmayan serbest gider: doğrudan kalkışa yazılır
        return [
            'payable_type' => get_class($date),
            'payable_id' => $date->getKey(),
            'category' => 'expense',
            'currency' => 'TRY',
            'counterparty_type' => null,
            'counterparty_id' => null,
            'counterparty_name' => null,
            'title' => 'Operasyon Gideri · ' . $context,
            'description' => $context,
        ];
    }

    // ─── Doğrulama ───────────────────────────────────────────────

    private function validatePayload(Request $request): array
    {
        $data = $request->validate([
            'title' => 'required|string|max:190',
            'description' => 'nullable|string|max:255',
            'category' => 'required|in:' . implode(',', array_keys(OperationExpense::CATEGORIES)),
            'planned_amount' => 'required|numeric|min:0|max:99999999.99',
            'currency' => 'required|string|size:3',
            'due_date' => 'nullable|date',
            'counterparty_type' => 'nullable|string',
            'counterparty_id' => 'nullable|integer',
            'counterparty_name' => 'nullable|string|max:190',
            'resource_schedule_id' => 'nullable|integer',
            'notes' => 'nullable|string|max:2000',
        ], [
            'title.required' => 'Kalem başlığı zorunludur.',
            'planned_amount.required' => 'Planlanan tutar zorunludur.',
        ]);

        $agencyId = $this->agencyId();

        // Muhatap yalnızca aynı acentenin kayıtlarına bağlanabilir
        if (!empty($data['counterparty_type']) && !array_key_exists($data['counterparty_type'], OperationExpense::COUNTERPARTY_TYPES)) {
            $data['counterparty_type'] = null;
            $data['counterparty_id'] = null;
        }

        if (($data['counterparty_type'] ?? null) === Supplier::class
            && !Supplier::byAgency($agencyId)->whereKey($data['counterparty_id'] ?? 0)->exists()) {
            $data['counterparty_type'] = null;
            $data['counterparty_id'] = null;
        }

        if (($data['counterparty_type'] ?? null) === User::class
            && !User::whereKey($data['counterparty_id'] ?? 0)->exists()) {
            $data['counterparty_type'] = null;
            $data['counterparty_id'] = null;
        }

        if (empty($data['counterparty_id'])) {
            $data['counterparty_type'] = null;
        }

        return $data;
    }
}
