<?php

namespace Modules\VetOneDemo\Services;

use App\User;
use App\Utils\BusinessUtil;
use Carbon\Carbon;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Str;
use Modules\PortalApi\Entities\PortalBrand;
use Modules\VetClinic\Services\DefaultRolesSeeder;
use Spatie\Permission\Models\Permission;
use Spatie\Permission\Models\Role;

/**
 * Turns a validated registration into a fully-working, branded 48-hour vet
 * demo tenant. Everything runs in one transaction on the default connection
 * (the VetOne cloud deploy's default DB is the vet project DB).
 *
 * Reuses core + module seams — no core edits:
 *   - App\Utils\BusinessUtil::createNewBusiness / newBusinessDefaultResources / addLocation
 *   - VetClinic\Services\DefaultRolesSeeder (also auto-run by VcBusinessObserver)
 *   - PortalApi\Entities\PortalBrand (drives the branded pet-owner + staff PWAs)
 *   - business.module_entitlements (SaasSwitcher gating)
 */
class DemoProvisioner
{
    /**
     * Distinct weekly schedule templates so the 3 demo doctors have genuinely
     * different availability (day sets, hours and slot sizes) and colours.
     * day_of_week: 0=Sun … 6=Sat.
     */
    protected array $doctorTemplates = [
        ['days' => [0, 1, 2, 3, 4], 'start' => '09:00', 'end' => '15:00', 'slot' => 30, 'color' => '#0d9488', 'spec' => 'General Practice'],
        ['days' => [6, 0, 1, 2, 3], 'start' => '12:00', 'end' => '20:00', 'slot' => 20, 'color' => '#f59e0b', 'spec' => 'Surgery'],
        ['days' => [1, 3, 5, 6],    'start' => '10:00', 'end' => '18:00', 'slot' => 15, 'color' => '#6366f1', 'spec' => 'Dermatology'],
        ['days' => [0, 2, 4],       'start' => '08:00', 'end' => '13:00', 'slot' => 20, 'color' => '#ec4899', 'spec' => 'Internal Medicine'],
    ];

    protected array $doctorNames = [
        ['Sarah', 'Hassan'], ['Ahmed', 'Fathy'], ['Mona', 'Adel'],
        ['Omar', 'Saleh'], ['Layla', 'Nabil'], ['Karim', 'Sobhy'],
    ];

    public function __construct(protected BusinessUtil $businessUtil)
    {
    }

    /**
     * @param  array  $in  Validated + prepared input from RegisterController:
     *   name, owner_first, owner_last, email, phone, whatsapp, slug, accent_hex,
     *   country, state, city, zip_code, landmark, website, logo (filename|null),
     *   logo_url (string|null), password, counts => [Doctor, Nurse, Receptionist, Accounting, HR]
     * @return array{business:\App\Business, owner:User, slug:string, owner_username:string}
     */
    public function provision(array $in): array
    {
        return DB::transaction(function () use ($in) {
            // Make the vet permission axis real BEFORE the business is created, so the
            // VcBusinessObserver's auto role-seed can attach them on the first pass.
            $this->ensureVetPermissions();

            $currencyId = DB::table('currencies')
                ->where('code', config('vetonedemo.defaults.currency_code', 'EGP'))
                ->value('id') ?? DB::table('currencies')->min('id') ?? 1;

            // 1. Owner user ------------------------------------------------------
            $ownerUsername = $this->uniqueUsername($in['slug'] . '-admin');
            $owner = User::create_user([
                'surname'    => '', // core: surname is a short title (Mr/Dr, max 10), not the last name
                'first_name' => $in['owner_first'],
                'last_name'  => $in['owner_last'] ?: '',
                'username'   => $ownerUsername,
                'email'      => $in['email'] ?: null,
                'password'   => $in['password'],
                'language'   => 'en',
            ]);

            // 2. Business --------------------------------------------------------
            $businessDetails = [
                'name'              => $in['name'],
                'currency_id'       => $currencyId,
                'time_zone'         => config('vetonedemo.defaults.time_zone', 'Africa/Cairo'),
                'fy_start_month'    => config('vetonedemo.defaults.fy_start_month', 1),
                'accounting_method' => config('vetonedemo.defaults.accounting_method', 'fifo'),
                'owner_id'          => $owner->id,
                'start_date'        => Carbon::now()->toDateString(),
                'enabled_modules'   => ['purchases', 'add_sale', 'pos_sale', 'stock_transfers', 'stock_adjustment', 'expenses'],
            ];
            if (! empty($in['logo'])) {
                $businessDetails['logo'] = $in['logo'];
            }
            $business = $this->businessUtil->createNewBusiness($businessDetails);

            $owner->business_id = $business->id;
            $owner->save();

            // 3. Default ERP resources + primary location ------------------------
            $this->businessUtil->newBusinessDefaultResources($business->id, $owner->id);

            $location = $this->businessUtil->addLocation($business->id, [
                'name'             => config('vetonedemo.defaults.location_name', 'Main Branch'),
                'country'          => $in['country'] ?? '',
                'state'            => $in['state'] ?? '',
                'city'             => $in['city'] ?? '',
                'zip_code'         => $in['zip_code'] ?? '',
                'landmark'         => $in['landmark'] ?? '',
                'website'          => $in['website'] ?? '',
                'mobile'           => $in['phone'] ?? '',
                'alternate_number' => $in['whatsapp'] ?? '',
            ]);
            if (! Permission::where('name', 'location.' . $location->id)->exists()) {
                Permission::create(['name' => 'location.' . $location->id, 'guard_name' => 'web']);
            }

            // 4. Demo lifecycle + module entitlements (raw JSON, no model cast) ---
            DB::table('business')->where('id', $business->id)->update([
                'module_entitlements' => json_encode(array_values(config('vetonedemo.entitlements', []))),
                'demo_slug'           => $in['slug'],
                'demo_created_at'     => Carbon::now(),
                'demo_expires_at'     => Carbon::now()->addHours((int) config('vetonedemo.demo.hours', 48)),
                'demo_status'         => 'active',
            ]);

            // 5. Vet roles (idempotent — observer may have run already) ----------
            DefaultRolesSeeder::seedForBusiness($business->id);

            // 6. Pet-owner + staff PWA brand -------------------------------------
            PortalBrand::create([
                'business_id'   => $business->id,
                'slug'          => $in['slug'],
                'name'          => $in['name'],
                'accent_hex'    => $in['accent_hex'] ?: config('vetonedemo.defaults.accent_hex', '#0d9488'),
                'logo_url'      => $in['logo_url'] ?? null,
                'contact_phone' => $in['phone'] ?? null,
                'contact_email' => $in['email'] ?: null,
                'address'       => $this->composeAddress($in),
                'login_methods' => ['otp', 'magic', 'password', 'staff'],
                'is_active'     => true,
            ]);

            // 7. Doctors (with distinct schedules) + other staff -----------------
            $doctorCount = max(1, (int) ($in['counts']['Doctor'] ?? config('vetonedemo.demo.default_doctor_count', 3)));
            $doctors = $this->createDoctors($business->id, $doctorCount);
            $this->createStaff($business->id, $in['counts'] ?? []);

            // 8. Clinical content: clone medicine catalog + light demo data ------
            $this->cloneMedicines($business->id);
            $this->seedClinicalDemo($business->id, $doctors);

            return [
                'business'       => $business,
                'owner'          => $owner,
                'slug'           => $in['slug'],
                'owner_username' => $ownerUsername,
            ];
        });
    }

    /* ─────────────────────────── helpers ──────────────────────────────── */

    protected function ensureVetPermissions(): void
    {
        $perms = collect(DefaultRolesSeeder::PRESETS)->flatten()->unique();
        foreach ($perms as $p) {
            Permission::findOrCreate($p, 'web');
        }
    }

    protected function uniqueUsername(string $base): string
    {
        $base = Str::slug($base) ?: 'user';
        if (strlen($base) < 4) {
            $base = str_pad($base, 4, '0');
        }
        $u = $base;
        $i = 1;
        while (User::where('username', $u)->exists()) {
            $u = $base . $i;
            $i++;
        }
        return $u;
    }

    protected function composeAddress(array $in): ?string
    {
        $parts = array_filter([
            $in['landmark'] ?? null,
            $in['city'] ?? null,
            $in['state'] ?? null,
            $in['country'] ?? null,
        ]);
        return $parts ? implode(', ', $parts) : null;
    }

    /** @return array<int,array{id:int,user_id:int,name:string}> */
    protected function createDoctors(int $bizId, int $count): array
    {
        $doctors = [];
        for ($i = 0; $i < $count; $i++) {
            $tpl   = $this->doctorTemplates[$i % count($this->doctorTemplates)];
            [$first, $last] = $this->doctorNames[$i % count($this->doctorNames)];
            $displayName = 'Dr. ' . $first . ' ' . $last;

            $user = $this->makeDemoUser($bizId, 'dr' . ($i + 1), 'Dr. ' . $first, $last, 'Doctor');

            $doctorId = DB::table('vc_doctors')->insertGetId([
                'business_id'      => $bizId,
                'user_id'          => $user->id,
                'department_id'    => null,
                'name'             => $displayName,
                'email'            => $user->email,
                'phone'            => null,
                'specialization'   => $tpl['spec'],
                'about'            => 'Demo veterinarian ( ' . $tpl['spec'] . ' ).',
                'consultation_fee' => 300,
                'status'           => 'active',
                'color'            => $tpl['color'],
                'created_at'       => now(),
                'updated_at'       => now(),
            ]);

            foreach ($tpl['days'] as $day) {
                DB::table('vc_doctor_schedules')->insert([
                    'doctor_id'             => $doctorId,
                    'department_id'         => null,
                    'day_of_week'           => $day,
                    'start_time'            => $tpl['start'] . ':00',
                    'end_time'              => $tpl['end'] . ':00',
                    'slot_duration_minutes' => $tpl['slot'],
                    'status'                => 'active',
                    'created_at'            => now(),
                    'updated_at'            => now(),
                ]);
            }

            $doctors[] = ['id' => $doctorId, 'user_id' => $user->id, 'name' => $displayName];
        }
        return $doctors;
    }

    protected function createStaff(int $bizId, array $counts): void
    {
        // Role base => already-a-vet-preset? (HR is not, so we create a bare role)
        $roles = ['Nurse', 'Receptionist', 'Accounting', 'HR'];
        foreach ($roles as $roleBase) {
            $n = (int) ($counts[$roleBase] ?? 0);
            if ($n <= 0) {
                continue;
            }
            $this->ensureRole($roleBase, $bizId);
            for ($k = 1; $k <= $n; $k++) {
                $this->makeDemoUser($bizId, strtolower($roleBase) . $k, $roleBase . ' ' . $k, 'Demo', $roleBase);
            }
        }
    }

    protected function ensureRole(string $roleBase, int $bizId): void
    {
        Role::firstOrCreate(
            ['name' => $roleBase . '#' . $bizId, 'guard_name' => 'web'],
            ['business_id' => $bizId, 'is_default' => 0, 'is_service_staff' => 0]
        );
    }

    /** Create an active, login-enabled demo user and assign it {Role}#{bizId}. */
    protected function makeDemoUser(int $bizId, string $handle, string $firstName, string $lastName, string $roleBase): User
    {
        $username = $this->uniqueUsername('demo-' . $bizId . '-' . $handle);
        $user = new User();
        $user->forceFill([
            'user_type'   => 'user',
            'surname'     => $lastName,
            'first_name'  => $firstName,
            'username'    => $username,
            'email'       => $username . '@demo.local',
            'password'    => Hash::make(Str::random(40)),
            'language'    => 'en',
            'business_id' => $bizId,
            'status'      => 'active',
            'allow_login' => 1,
        ])->save();

        $this->ensureRole($roleBase, $bizId);
        try {
            $user->assignRole($roleBase . '#' . $bizId);
        } catch (\Throwable $e) {
            Log::warning("VetOneDemo: assignRole {$roleBase}#{$bizId} failed: " . $e->getMessage());
        }

        return $user;
    }

    /**
     * Clone the per-business medicine catalog from a template business into the
     * new tenant. Medicine categories are seeded globally (business_id NULL) so
     * category_id references remain valid across tenants.
     */
    protected function cloneMedicines(int $bizId): void
    {
        try {
            $templateBiz = DB::table('vc_medicines')->where('business_id', '!=', $bizId)->min('business_id');
            if (! $templateBiz) {
                return;
            }
            $rows = DB::table('vc_medicines')->where('business_id', $templateBiz)->get();
            $now  = now();
            foreach ($rows as $r) {
                DB::table('vc_medicines')->insert([
                    'business_id'  => $bizId,
                    'category_id'  => $r->category_id,
                    'name'         => $r->name,
                    'generic_name' => $r->generic_name,
                    'company'      => $r->company,
                    'unit'         => $r->unit,
                    'product_id'   => null,
                    'description'  => $r->description,
                    'status'       => 'active',
                    'created_at'   => $now,
                    'updated_at'   => $now,
                ]);
            }
        } catch (\Throwable $e) {
            Log::warning('VetOneDemo: cloneMedicines failed: ' . $e->getMessage());
        }
    }

    /**
     * A little demo clinical data so the calendar/records look alive: two pet
     * owners (contacts + owner details), a pet each, and one upcoming
     * appointment per doctor. Fully guarded — sample data must never abort a
     * signup, so any schema surprise is logged and swallowed.
     */
    protected function seedClinicalDemo(int $bizId, array $doctors): void
    {
        try {
            $owners = [
                ['name' => 'John Anderson', 'pet' => 'Buddy', 'species' => 'Dog', 'breed' => 'Golden Retriever', 'gender' => 'male'],
                ['name' => 'Mariam Youssef', 'pet' => 'Luna',  'species' => 'Cat', 'breed' => 'Domestic Shorthair', 'gender' => 'female'],
            ];

            $seq = (int) DB::table('contacts')->where('business_id', $bizId)->count();

            foreach ($owners as $idx => $o) {
                $seq++;
                $contactId = DB::table('contacts')->insertGetId([
                    'business_id'    => $bizId,
                    'type'           => 'customer',
                    'name'           => $o['name'],
                    'contact_id'     => 'CO' . str_pad((string) $seq, 4, '0', STR_PAD_LEFT),
                    'is_default'     => 0,
                    'credit_limit'   => 0,
                    'created_by'     => $doctors[0]['user_id'] ?? null,
                    'created_at'     => now(),
                    'updated_at'     => now(),
                ]);

                DB::table('vc_pet_owner_details')->insert([
                    'business_id' => $bizId,
                    'contact_id'  => $contactId,
                    'created_at'  => now(),
                    'updated_at'  => now(),
                ]);

                $petId = DB::table('vc_pets')->insertGetId([
                    'business_id' => $bizId,
                    'contact_id'  => $contactId,
                    'petname'     => $o['pet'],
                    'species'     => $o['species'],
                    'breed'       => $o['breed'],
                    'gender'      => $o['gender'],
                    'status'      => 'active',
                    'created_at'  => now(),
                    'updated_at'  => now(),
                ]);

                $doctor = $doctors[$idx % max(1, count($doctors))] ?? null;
                if ($doctor) {
                    DB::table('vc_appointments')->insert([
                        'business_id'      => $bizId,
                        'contact_id'       => $contactId,
                        'pet_id'           => $petId,
                        'doctor_id'        => $doctor['id'],
                        'department_id'    => null,
                        'ref_no'           => 'APT-' . strtoupper(Str::random(8)),
                        'appointment_date' => Carbon::now()->addDays($idx + 1)->toDateString(),
                        'appointment_time' => '10:00:00',
                        'duration_minutes' => 30,
                        'reason'           => 'Wellness check-up',
                        'type'             => 'checkup',
                        'status'           => 'confirmed',
                        'created_at'       => now(),
                        'updated_at'       => now(),
                    ]);
                }
            }
        } catch (\Throwable $e) {
            Log::warning('VetOneDemo: seedClinicalDemo skipped: ' . $e->getMessage());
        }
    }
}
