<?php

namespace App\Http\Controllers\Admin;

use App\Http\Controllers\Controller;
use App\Models\Category;
use App\Models\Coupon;
use App\Models\CouponRedemption;
use App\Models\Network;
use App\Services\AuditLogService;
use App\Services\CouponService;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Validation\Rule;
use Inertia\Inertia;
use Inertia\Response;

class CouponController extends Controller
{
    public function index(Request $request): Response
    {
        $filters = $request->only(['q', 'status']);

        $coupons = Coupon::query()
            ->withCount([
                'redemptions as active_usage_count' => fn ($q) => $q->whereIn('status', CouponRedemption::ACTIVE_STATUSES),
                'redemptions as applied_usage_count' => fn ($q) => $q->where('status', CouponRedemption::STATUS_APPLIED),
            ])
            ->when($request->filled('q'), function ($query) use ($request) {
                $term = trim((string) $request->string('q'));
                $query->where(function ($subQuery) use ($term) {
                    $subQuery->where('name', 'like', "%{$term}%")
                        ->orWhere('code', 'like', "%{$term}%");
                });
            })
            ->when($request->filled('status'), fn ($query) => $query->where('status', (string) $request->string('status')))
            ->latest()
            ->paginate(20)
            ->withQueryString()
            ->through(function (Coupon $coupon) {
                return [
                    'id' => $coupon->id,
                    'name' => $coupon->name,
                    'code' => $coupon->code,
                    'status' => $coupon->status,
                    'type' => $coupon->type,
                    'value' => (float) $coupon->value,
                    'min_amount' => $coupon->min_amount !== null ? (float) $coupon->min_amount : null,
                    'max_discount' => $coupon->max_discount !== null ? (float) $coupon->max_discount : null,
                    'usage_limit' => $coupon->usage_limit,
                    'per_user_limit' => $coupon->per_user_limit,
                    'is_stackable' => (bool) $coupon->is_stackable,
                    'applicable_category_ids' => array_values(array_map('intval', (array) ($coupon->applicable_category_ids_json ?? []))),
                    'applicable_network_ids' => array_values(array_map('intval', (array) ($coupon->applicable_network_ids_json ?? []))),
                    'applicable_customer_tiers' => array_values((array) ($coupon->applicable_customer_tiers_json ?? [])),
                    'starts_at' => optional($coupon->starts_at)?->format('Y-m-d\TH:i'),
                    'ends_at' => optional($coupon->ends_at)?->format('Y-m-d\TH:i'),
                    'created_at' => optional($coupon->created_at)?->toDateTimeString(),
                    'updated_at' => optional($coupon->updated_at)?->toDateTimeString(),
                    'active_usage_count' => (int) ($coupon->active_usage_count ?? 0),
                    'applied_usage_count' => (int) ($coupon->applied_usage_count ?? 0),
                ];
            });

        $summary = [
            'total' => Coupon::query()->count(),
            'active' => Coupon::query()->where('status', Coupon::STATUS_ACTIVE)->count(),
            'inactive' => Coupon::query()->where('status', Coupon::STATUS_INACTIVE)->count(),
            'active_redemptions' => CouponRedemption::query()->whereIn('status', CouponRedemption::ACTIVE_STATUSES)->count(),
            'applied_redemptions' => CouponRedemption::query()->where('status', CouponRedemption::STATUS_APPLIED)->count(),
            'total_discount' => (string) (DB::table('coupon_redemptions')
                ->where('status', CouponRedemption::STATUS_APPLIED)
                ->selectRaw('COALESCE(SUM(discount_amount),0) as total')
                ->value('total') ?? '0'),
        ];

        return Inertia::render('Admin/Coupons/Index', [
            'coupons' => $coupons,
            'filters' => $filters,
            'statuses' => Coupon::STATUSES,
            'types' => Coupon::TYPES,
            'tiers' => \App\Models\User::CUSTOMER_TIERS,
            'categories' => Category::query()->orderBy('name')->get(['id', 'name', 'slug']),
            'networks' => Network::query()->orderBy('name')->get(['id', 'name', 'slug']),
            'summary' => $summary,
        ]);
    }

    public function store(
        Request $request,
        CouponService $couponService,
        AuditLogService $auditLogService
    ): RedirectResponse {
        $data = $this->validateCouponPayload($request);
        $coupon = Coupon::create($this->buildCouponAttributes($data, $request, $couponService, false));

        $auditLogService->log(
            admin: $request->user(),
            action: 'coupons.created',
            entity: $coupon,
            oldValues: null,
            newValues: $coupon->fresh()->toArray(),
            ipAddress: $request->ip(),
            userAgent: $request->userAgent()
        );

        return back()->with('success', 'Coupon created successfully.');
    }

    public function update(
        Request $request,
        Coupon $coupon,
        CouponService $couponService,
        AuditLogService $auditLogService
    ): RedirectResponse {
        $before = $coupon->toArray();
        $data = $this->validateCouponPayload($request, $coupon);

        $coupon->update($this->buildCouponAttributes($data, $request, $couponService, true, $coupon));

        $auditLogService->log(
            admin: $request->user(),
            action: 'coupons.updated',
            entity: $coupon->fresh(),
            oldValues: $before,
            newValues: $coupon->fresh()->toArray(),
            ipAddress: $request->ip(),
            userAgent: $request->userAgent()
        );

        return back()->with('success', 'Coupon updated successfully.');
    }

    protected function validateCouponPayload(Request $request, ?Coupon $coupon = null): array
    {
        return $request->validate([
            'name' => ['required', 'string', 'max:120'],
            'code' => [
                'required',
                'string',
                'max:64',
                'regex:/^[A-Za-z0-9_-]+$/',
                Rule::unique('coupons', 'code')->ignore($coupon?->id),
            ],
            'status' => ['required', Rule::in(Coupon::STATUSES)],
            'type' => ['required', Rule::in(Coupon::TYPES)],
            'value' => ['required', 'numeric', 'min:0.01', 'max:1000000'],
            'min_amount' => ['nullable', 'numeric', 'min:0', 'max:100000000'],
            'max_discount' => ['nullable', 'numeric', 'min:0', 'max:100000000'],
            'usage_limit' => ['nullable', 'integer', 'min:1', 'max:100000000'],
            'per_user_limit' => ['nullable', 'integer', 'min:1', 'max:100000000'],
            'is_stackable' => ['nullable', 'boolean'],
            'applicable_category_ids' => ['nullable', 'array'],
            'applicable_category_ids.*' => ['integer', 'exists:categories,id'],
            'applicable_network_ids' => ['nullable', 'array'],
            'applicable_network_ids.*' => ['integer', 'exists:networks,id'],
            'applicable_customer_tiers' => ['nullable', 'array'],
            'applicable_customer_tiers.*' => ['string', Rule::in(\App\Models\User::CUSTOMER_TIERS)],
            'starts_at' => ['nullable', 'date'],
            'ends_at' => ['nullable', 'date', 'after_or_equal:starts_at'],
        ]);
    }

    protected function buildCouponAttributes(
        array $data,
        Request $request,
        CouponService $couponService,
        bool $isUpdate = false,
        ?Coupon $coupon = null
    ): array {
        $normalizedCode = $couponService->normalizeCode((string) $data['code']);

        return [
            'name' => trim((string) $data['name']),
            'code' => $normalizedCode,
            'status' => (string) $data['status'],
            'type' => (string) $data['type'],
            'value' => round((float) $data['value'], 2),
            'min_amount' => isset($data['min_amount']) ? round((float) $data['min_amount'], 2) : null,
            'max_discount' => isset($data['max_discount']) ? round((float) $data['max_discount'], 2) : null,
            'usage_limit' => $data['usage_limit'] ?? null,
            'per_user_limit' => $data['per_user_limit'] ?? null,
            'is_stackable' => (bool) ($data['is_stackable'] ?? false),
            'applicable_category_ids_json' => array_values(array_unique(array_map('intval', $data['applicable_category_ids'] ?? []))) ?: null,
            'applicable_network_ids_json' => array_values(array_unique(array_map('intval', $data['applicable_network_ids'] ?? []))) ?: null,
            'applicable_customer_tiers_json' => array_values(array_unique(array_map(fn ($v) => strtolower((string) $v), $data['applicable_customer_tiers'] ?? []))) ?: null,
            'starts_at' => filled($data['starts_at'] ?? null) ? $data['starts_at'] : null,
            'ends_at' => filled($data['ends_at'] ?? null) ? $data['ends_at'] : null,
            'updated_by_admin_id' => $request->user()->id,
            'created_by_admin_id' => $isUpdate ? ($coupon?->created_by_admin_id ?? $request->user()->id) : $request->user()->id,
        ];
    }
}

