<?php

namespace App\Services;

use App\Models\MexcoEventLicense;
use App\Models\MexcoOrganisationUser;
use App\Models\MexcoPlan;
use App\Models\MeetupEvent;
use App\Models\MeetupParticipant;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;
use Illuminate\Validation\ValidationException;

class DemoService
{
    public function __construct(
        private SettingsService $settings,
        private MeetupService $meetup,
        private EntitlementService $entitlements,
    ) {}

    public function isEnabled(): bool
    {
        return (bool) $this->settings->get('trial', 'enabled', true);
    }

    public function durationDays(): int
    {
        return max(1, (int) $this->settings->get('trial', 'duration_days', 10));
    }

    public function plan(): MexcoPlan
    {
        $code = (string) $this->settings->get('trial', 'plan_code', 'demo');

        $plan = MexcoPlan::query()->where('code', $code)->where('is_active', true)->first()
            ?? MexcoPlan::query()->where('code', 'demo')->where('is_active', true)->first();

        if (! $plan) {
            throw ValidationException::withMessages([
                'demo' => 'Demo plan is not configured. Contact support.',
            ]);
        }

        return $plan;
    }

    public function limitations(): array
    {
        $plan = $this->plan();

        return [
            'duration_days' => $this->durationDays(),
            'participants' => $plan->participant_limit,
            'sessions' => $plan->session_limit,
            'booths' => $plan->booth_limit,
            'storage_mb' => $plan->storage_limit_mb,
            'features' => 'Full MeXCo feature set',
        ];
    }

    /**
     * Default shared sandbox credentials for public Try Demo.
     *
     * @return array{
     *   event_id: string,
     *   event_name: string,
     *   organiser: array{username: string, password: string},
     *   participants: list<array{email: string, name: string, org: string, ic: string, phone: string, role: string}>
     * }
     */
    public function defaultAccounts(): array
    {
        $cfg = config('mexco.demo_sandbox', []);

        return [
            'event_id' => (string) ($cfg['event_id'] ?? 'mexcodemo'),
            'event_name' => (string) ($cfg['event_name'] ?? 'MeXCo Demo Experience'),
            'organiser' => [
                'username' => (string) data_get($cfg, 'organiser.username', 'organiser@demo.mexco'),
                'password' => (string) data_get($cfg, 'organiser.password', '1234'),
            ],
            'participants' => array_values($cfg['participants'] ?? []),
        ];
    }

    public function primaryVisitor(): ?array
    {
        $participants = $this->defaultAccounts()['participants'];

        foreach ($participants as $participant) {
            if (($participant['role'] ?? '') === 'VISITOR') {
                return $participant;
            }
        }

        return $participants[0] ?? null;
    }

    /**
     * Ensure the shared public demo event exists with default organiser + participants.
     */
    public function ensureSharedSandbox(): MeetupEvent
    {
        $accounts = $this->defaultAccounts();
        $plan = $this->plan();
        $days = max($this->durationDays(), 365);

        return DB::transaction(function () use ($accounts, $plan, $days) {
            $event = MeetupEvent::query()->updateOrCreate(
                ['id' => $accounts['event_id']],
                [
                    'name' => $accounts['event_name'],
                    'description' => 'Shared MeXCo sandbox for trying Organizer and Pass.',
                    'event_type' => 'demo',
                    'expected_participants' => $plan->participant_limit,
                    'venue' => 'Demo venue',
                    'dates' => now()->format('j M Y').' – ongoing demo',
                    'event_name' => $accounts['event_name'],
                    'short_name' => 'MeXCo Demo',
                    'passcode' => $accounts['organiser']['password'],
                    'organiser_username' => $accounts['organiser']['username'],
                    'organiser_password' => $accounts['organiser']['password'],
                    'allow_voting' => true,
                    'allow_floor_plan' => true,
                    'is_virtual' => false,
                    'starts_at' => now()->subDay(),
                    'ends_at' => now()->addDays($days),
                ]
            );

            $this->meetup->bootstrapEventDefaults($event->id);
            $this->seedDefaultParticipants($event->id, $accounts['participants']);

            $license = $this->entitlements->assignPlan($event->id, $plan->code);
            $license->update([
                'status' => 'active',
                'price_cents' => 0,
                'discount_cents' => 0,
                'is_trial' => true,
                'is_campus_sponsored' => false,
                'starts_at' => now()->subDay(),
                'ends_at' => now()->addDays($days),
                'trial_ends_at' => now()->addDays($days),
                'activated_at' => now()->subDay(),
                'metadata' => array_merge($license->metadata ?? [], [
                    'demo' => true,
                    'shared_sandbox' => true,
                ]),
            ]);

            $this->entitlements->rebuildEntitlements($event->id);

            return $event->fresh(['license.plan']);
        });
    }

    public function organisationHasDemo(int $organisationId): bool
    {
        return MexcoEventLicense::query()
            ->where('is_trial', true)
            ->whereHas('event', fn ($q) => $q->where('organisation_id', $organisationId))
            ->exists();
    }

    public function canStart(MexcoOrganisationUser $user): bool
    {
        if (! $this->isEnabled()) {
            return false;
        }

        if (! $user->isOrganisationAdmin()) {
            return false;
        }

        if ((bool) $this->settings->get('trial', 'one_per_organisation', true)
            && $this->organisationHasDemo((int) $user->organisation_id)) {
            return false;
        }

        return true;
    }

    /**
     * @return array{event: MeetupEvent, organiser_password: string, accounts: array}
     */
    public function start(MexcoOrganisationUser $user, ?string $eventName = null): array
    {
        if (! $this->isEnabled()) {
            throw ValidationException::withMessages([
                'demo' => 'Free demo is currently unavailable.',
            ]);
        }

        if (! $user->isOrganisationAdmin()) {
            throw ValidationException::withMessages([
                'demo' => 'Only organisation admins can start a demo.',
            ]);
        }

        if ((bool) $this->settings->get('trial', 'one_per_organisation', true)
            && $this->organisationHasDemo((int) $user->organisation_id)) {
            throw ValidationException::withMessages([
                'demo' => 'Your organisation already used its free demo. Choose a paid plan to continue.',
            ]);
        }

        $plan = $this->plan();
        $days = $this->durationDays();
        $organisation = $user->organisation;
        $defaults = $this->defaultAccounts();
        $password = $defaults['organiser']['password'];
        $name = trim($eventName ?: 'MeXCo Demo Event');
        $username = 'organiser.'.$organisation->id.'@demo.mexco';

        $event = DB::transaction(function () use ($user, $organisation, $plan, $days, $password, $name, $username, $defaults) {
            $event = $this->meetup->createEvent([
                'organisation_id' => $organisation->id,
                'name' => $name,
                'description' => 'Limited free demo with full MeXCo features.',
                'event_type' => 'demo',
                'expected_participants' => $plan->participant_limit,
                'contact_person' => $user->name,
                'contact_email' => $user->email,
                'venue' => 'Demo venue',
                'dates' => now()->format('j M Y').' – '.now()->addDays($days)->format('j M Y'),
                'organiser_username' => $username,
                'organiser_password' => $password,
                'passcode' => $password,
                'allow_voting' => true,
                'allow_floor_plan' => true,
                'starts_at' => now(),
                'ends_at' => now()->addDays($days),
            ]);

            $this->seedDefaultParticipants($event->id, $defaults['participants']);

            $license = $this->entitlements->assignPlan($event->id, $plan->code);

            $license->update([
                'status' => 'active',
                'price_cents' => 0,
                'discount_cents' => 0,
                'is_trial' => true,
                'is_campus_sponsored' => false,
                'starts_at' => now(),
                'ends_at' => now()->addDays($days),
                'trial_ends_at' => now()->addDays($days),
                'activated_at' => now(),
                'metadata' => array_merge($license->metadata ?? [], [
                    'demo' => true,
                    'started_by' => $user->id,
                    'default_password' => true,
                ]),
            ]);

            $this->entitlements->rebuildEntitlements($event->id);

            return $event->fresh(['license.plan', 'organisation']);
        });

        return [
            'event' => $event,
            'organiser_password' => $password,
            'accounts' => [
                'organiser' => [
                    'username' => $event->organiser_username,
                    'password' => $password,
                ],
                'participants' => $defaults['participants'],
            ],
        ];
    }

    public function activeDemoForOrganisation(int $organisationId): ?MexcoEventLicense
    {
        return MexcoEventLicense::query()
            ->with(['event', 'plan'])
            ->where('is_trial', true)
            ->where('status', 'active')
            ->whereHas('event', fn ($q) => $q->where('organisation_id', $organisationId))
            ->orderByDesc('id')
            ->first();
    }

    protected function seedDefaultParticipants(string $eventId, array $participants): void
    {
        foreach ($participants as $row) {
            $email = strtolower(trim((string) ($row['email'] ?? '')));
            if ($email === '') {
                continue;
            }

            $slug = Str::slug(str_replace('@', '_', $email), '_');

            MeetupParticipant::query()->updateOrCreate(
                ['event_id' => $eventId, 'slug' => $slug],
                [
                    'email' => $email,
                    'name' => $row['name'] ?? 'Demo Participant',
                    'org' => $row['org'] ?? 'MeXCo Demo',
                    'ic' => $row['ic'] ?? '900101123456',
                    'phone' => $row['phone'] ?? null,
                    'role' => $row['role'] ?? 'VISITOR',
                    'is_committee' => (bool) ($row['is_committee'] ?? in_array(strtoupper((string) ($row['role'] ?? '')), ['COMMITTEE', 'ORGANISER'], true)),
                    'booth_stamps' => [],
                    'session_attendance' => [],
                ]
            );
        }
    }
}
