<?php

namespace App\Http\Controllers\Api\V1;

use App\Http\Controllers\Controller;
use App\Models\Customer;
use App\Support\ApiResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;

final class SubscriptionController extends Controller
{
    public function store(Request $request)
    {
        $data = $request->validate([
            'plan_id' => ['required', 'string', 'max:40'],
            'customer_id' => ['nullable', 'string', 'max:40'],
            'customer.email' => ['required_without:customer_id', 'email', 'max:255'],
            'customer.name' => ['nullable', 'string', 'max:255'],
            'metadata' => ['nullable', 'array'],
        ]);

        $merchant = $request->attributes->get('merchant');
        $environment = $request->attributes->get('environment');
        $customer = null;

        if (! empty($data['customer_id'])) {
            $customer = Customer::query()
                ->where('public_id', $data['customer_id'])
                ->where('merchant_id', $merchant->id)
                ->where('environment', $environment)
                ->where('status', 'active')
                ->first();

            if (! $customer) {
                return ApiResponse::error('CUSTOMER_NOT_FOUND', 'The requested customer does not exist or is archived.', 404);
            }
        }

        $plan = DB::table('subscription_plans')
            ->where('public_id', $data['plan_id'])
            ->where('merchant_id', $merchant->id)
            ->where('environment', $environment)
            ->where('status', 'active')
            ->first();

        if (! $plan) {
            return ApiResponse::error('PLAN_NOT_FOUND', 'The requested subscription plan does not exist.', 404);
        }

        $start = now();
        $end = $this->periodEnd($start, $plan->interval, (int) $plan->interval_count);
        $id = DB::table('subscriptions')->insertGetId([
            'public_id' => 'sub_'.Str::ulid(),
            'merchant_id' => $merchant->id,
            'subscription_plan_id' => $plan->id,
            'customer_id' => $customer?->id,
            'customer_name' => $data['customer']['name'] ?? $customer?->name,
            'customer_email' => $data['customer']['email'] ?? $customer?->email,
            'status' => 'active',
            'current_period_start' => $start,
            'current_period_end' => $end,
            'metadata' => json_encode($data['metadata'] ?? [], JSON_UNESCAPED_SLASHES),
            'created_at' => now(),
            'updated_at' => now(),
        ]);

        $subscription = DB::table('subscriptions')
            ->leftJoin('customers', 'customers.id', '=', 'subscriptions.customer_id')
            ->where('subscriptions.id', $id)
            ->select('subscriptions.*', 'customers.public_id as customer_public_id')
            ->first();

        return ApiResponse::success($this->resource($subscription, $plan), 201);
    }

    public function index(Request $request)
    {
        $rows = DB::table('subscriptions')
            ->join('subscription_plans', 'subscription_plans.id', '=', 'subscriptions.subscription_plan_id')
            ->leftJoin('customers', 'customers.id', '=', 'subscriptions.customer_id')
            ->where('subscriptions.merchant_id', $request->attributes->get('merchant')->id)
            ->select('subscriptions.*', 'subscription_plans.public_id as plan_public_id', 'subscription_plans.name as plan_name', 'customers.public_id as customer_public_id')
            ->latest('subscriptions.id')
            ->get()
            ->map(fn ($row) => $this->resource($row));

        return ApiResponse::success($rows);
    }

    public function cancel(Request $request, string $id)
    {
        $subscription = DB::table('subscriptions')
            ->where('public_id', $id)
            ->where('merchant_id', $request->attributes->get('merchant')->id)
            ->first();

        if (! $subscription) {
            return ApiResponse::error('SUBSCRIPTION_NOT_FOUND', 'The requested subscription does not exist.', 404);
        }

        DB::table('subscriptions')->where('id', $subscription->id)->update([
            'status' => 'cancelled',
            'cancelled_at' => now(),
            'updated_at' => now(),
        ]);

        $subscription = DB::table('subscriptions')
            ->leftJoin('customers', 'customers.id', '=', 'subscriptions.customer_id')
            ->where('subscriptions.id', $subscription->id)
            ->select('subscriptions.*', 'customers.public_id as customer_public_id')
            ->first();

        return ApiResponse::success($this->resource($subscription));
    }

    private function periodEnd(Carbon $start, string $interval, int $count): Carbon
    {
        return match ($interval) {
            'day' => $start->copy()->addDays($count),
            'week' => $start->copy()->addWeeks($count),
            'year' => $start->copy()->addYears($count),
            default => $start->copy()->addMonths($count),
        };
    }

    private function resource(object $subscription, ?object $plan = null): array
    {
        return [
            'id' => $subscription->public_id,
            'plan_id' => $plan?->public_id ?? ($subscription->plan_public_id ?? null),
            'plan_name' => $plan?->name ?? ($subscription->plan_name ?? null),
            'customer_id' => $subscription->customer_public_id ?? null,
            'customer' => ['name' => $subscription->customer_name, 'email' => $subscription->customer_email],
            'status' => $subscription->status,
            'current_period_start' => $subscription->current_period_start,
            'current_period_end' => $subscription->current_period_end,
            'cancelled_at' => $subscription->cancelled_at,
            'metadata' => json_decode($subscription->metadata ?? '[]', true) ?: [],
            'created_at' => $subscription->created_at,
        ];
    }
}
