<?php

namespace App\Http\Controllers\Api\Mobile;

use App\Http\Controllers\Controller;
use App\Models\AbTestVariant;
use App\Models\Campaign;
use App\Services\Campaign\ABTestService;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Validator;

/**
 * Mobile A/B test variant management.
 *
 * A campaign with type='ab_test' has 2+ variants on `ab_test_variants`
 * (campaign_id, variant, subject, body_html, percentage, is_winner).
 * SendCampaignEmailJob picks the variant per recipient and the
 * ABTestService eventually marks a winner based on open/click rates.
 *
 * The web composer manages variants via Livewire. This controller is
 * the equivalent surface for the Flutter app — it accepts the same
 * shape ABTestService::setupTest() consumes so the underlying
 * percentage-validation + transaction stays consistent across UIs.
 *
 * Endpoints (mounted under /api/v1/mobile/campaigns/{id}):
 *   GET    /variants                       list current variants
 *   POST   /variants                       replace all variants atomically
 *   PUT    /variants/{variant}             update one variant (subject/body/percentage)
 *   DELETE /variants/{variant}             remove one variant
 *   POST   /variants/{variant}/winner      mark a variant the winner
 */
class CampaignVariantsController extends Controller
{
    public function __construct(
        private readonly ABTestService $abTestService,
    ) {}

    public function index(Request $request, int $id): JsonResponse
    {
        $campaign = $this->resolveCampaign($request, $id);
        if (!$campaign instanceof Campaign) return $campaign;

        $variants = $campaign->abTestVariants()->orderBy('variant')->get();
        return response()->json([
            'data' => $variants->map(fn (AbTestVariant $v) => $this->shape($v))->all(),
            'meta' => [
                'total_percentage' => (int) $variants->sum('percentage'),
                'has_winner'       => $variants->contains(fn ($v) => (bool) $v->is_winner),
            ],
        ]);
    }

    /**
     * POST /variants — replace ALL variants atomically.
     *
     * Atomicity is delegated to ABTestService::setupTest which wraps
     * the delete + insert in a DB transaction. Percentages must sum
     * to 100 and there must be at least 2 variants — the service
     * throws InvalidArgumentException with a customer-readable
     * message that we surface as a 422.
     *
     * Also flips the campaign's `type` to 'ab_test' so subsequent
     * pre-flight checks in CampaignService::sendCampaign route through
     * the AB-aware code path.
     */
    public function replace(Request $request, int $id): JsonResponse
    {
        $campaign = $this->resolveCampaign($request, $id);
        if (!$campaign instanceof Campaign) return $campaign;

        if (!in_array($campaign->status, ['draft', 'scheduled'], true)) {
            return response()->json([
                'message' => "Cannot edit variants on a campaign with status '{$campaign->status}'.",
            ], 422);
        }

        $data = Validator::make($request->all(), [
            'variants'                 => ['required', 'array', 'min:2', 'max:10'],
            'variants.*.variant'       => ['required', 'string', 'max:5'], // 'A', 'B', 'C', …
            'variants.*.subject'       => ['required', 'string', 'max:500'],
            'variants.*.body_html'     => ['nullable', 'string'],
            'variants.*.percentage'    => ['required', 'integer', 'min:1', 'max:100'],
        ])->validate();

        try {
            $this->abTestService->setupTest($campaign, $data['variants']);
        } catch (\InvalidArgumentException $e) {
            return response()->json(['message' => $e->getMessage()], 422);
        }

        $campaign->refresh();
        $variants = $campaign->abTestVariants()->orderBy('variant')->get();

        return response()->json([
            'message' => __('A/B variants saved.'),
            'data'    => $variants->map(fn (AbTestVariant $v) => $this->shape($v))->all(),
        ], 201);
    }

    /**
     * PUT /variants/{variant} — update one variant by its letter ('A'/'B'/…).
     *
     * Used by the mobile editor's "edit this variant" sheet. Percentage
     * changes are validated in isolation here; the caller is expected
     * to keep the sum at 100 across the other variants — POST /variants
     * is the right call when restructuring the whole split.
     */
    public function update(Request $request, int $id, string $variant): JsonResponse
    {
        $campaign = $this->resolveCampaign($request, $id);
        if (!$campaign instanceof Campaign) return $campaign;

        if (!in_array($campaign->status, ['draft', 'scheduled'], true)) {
            return response()->json([
                'message' => "Cannot edit variants on a campaign with status '{$campaign->status}'.",
            ], 422);
        }

        $row = $campaign->abTestVariants()->where('variant', $variant)->first();
        if (!$row) {
            return response()->json(['message' => 'Variant not found.'], 404);
        }

        $data = Validator::make($request->all(), [
            'subject'    => ['sometimes', 'required', 'string', 'max:500'],
            'body_html'  => ['sometimes', 'nullable', 'string'],
            'percentage' => ['sometimes', 'required', 'integer', 'min:1', 'max:100'],
        ])->validate();

        $row->update($data);

        return response()->json([
            'message' => __('Variant updated.'),
            'data'    => $this->shape($row->fresh()),
        ]);
    }

    public function destroy(Request $request, int $id, string $variant): JsonResponse
    {
        $campaign = $this->resolveCampaign($request, $id);
        if (!$campaign instanceof Campaign) return $campaign;

        if (!in_array($campaign->status, ['draft', 'scheduled'], true)) {
            return response()->json([
                'message' => "Cannot edit variants on a campaign with status '{$campaign->status}'.",
            ], 422);
        }

        $row = $campaign->abTestVariants()->where('variant', $variant)->first();
        if (!$row) {
            return response()->json(['message' => 'Variant not found.'], 404);
        }

        // Block deletion when only 2 variants remain — an "AB test" with
        // a single variant is just a regular campaign and would mislead
        // the recipient-assignment logic.
        if ($campaign->abTestVariants()->count() <= 2) {
            return response()->json([
                'message' => 'An A/B test requires at least 2 variants. Switch the campaign type to "regular" before removing.',
            ], 422);
        }

        $row->delete();
        return response()->json(['message' => __('Variant removed.')]);
    }

    /**
     * POST /variants/{variant}/winner
     *
     * Manually mark a variant the winner. Typically the system marks
     * winners automatically once the sample size criteria are hit
     * (handled by ABTestService); this endpoint is the manual override
     * for the Flutter UI's "Send remaining audience this variant" CTA.
     */
    public function markWinner(Request $request, int $id, string $variant): JsonResponse
    {
        $campaign = $this->resolveCampaign($request, $id);
        if (!$campaign instanceof Campaign) return $campaign;

        $row = $campaign->abTestVariants()->where('variant', $variant)->first();
        if (!$row) {
            return response()->json(['message' => 'Variant not found.'], 404);
        }

        // One winner only. Flip everything to false then this row true
        // in a single statement so concurrent admins can't both crown
        // a variant.
        $campaign->abTestVariants()->update(['is_winner' => false]);
        $row->update(['is_winner' => true]);

        return response()->json([
            'message' => __('Variant :v marked as winner.', ['v' => $variant]),
            'data'    => $this->shape($row->fresh()),
        ]);
    }

    // ── helpers ──────────────────────────────────────────────────

    private function resolveCampaign(Request $request, int $id): Campaign|JsonResponse
    {
        $workspaceId = $request->user()->active_workspace_id;
        if (!$workspaceId) {
            return response()->json(['message' => 'No active workspace.'], 422);
        }
        $campaign = Campaign::where('workspace_id', $workspaceId)->find($id);
        if (!$campaign) {
            return response()->json(['message' => 'Campaign not found.'], 404);
        }
        return $campaign;
    }

    private function shape(AbTestVariant $v): array
    {
        return [
            'id'         => $v->id,
            'variant'    => $v->variant,
            'subject'    => $v->subject,
            'body_html'  => $v->body_html,
            'percentage' => (int) $v->percentage,
            'is_winner'  => (bool) $v->is_winner,
            'created_at' => $v->created_at?->toIso8601String(),
            'updated_at' => $v->updated_at?->toIso8601String(),
        ];
    }
}
