<?php

namespace Modules\MedRegistry\Support;

use Illuminate\Support\Str;
use Modules\MedRegistry\Entities\MedregModule;
use Nwidart\Modules\Facades\Module;

/**
 * Scans every module's module.json via the nwidart Module facade and upserts
 * one medreg_modules catalog row per module (P1-T3 manifest convention + retrofit).
 *
 * Metadata source, in order of precedence:
 *   1. the module's own `extra.medical` block in module.json (new convention,
 *      see Modules/MedRegistry/README.md) — modules MAY adopt it, none are edited;
 *   2. the CURATED map below (retro-fit for pre-existing modules, zero edits);
 *   3. safe defaults (category core, installable_per tenant, no spec_key, sort last).
 *
 * Shared by MedregCatalogSeeder and the `medreg:sync-catalog` command.
 */
class CatalogSync
{
    /**
     * Curated retro-fit metadata for modules that pre-date the extra.medical
     * convention. Keys: category, installable_per, spec_key, dependencies,
     * settings_schema, sort_order. spec_keys are SIFA spec module ids.
     *
     * Specialty *Clinic modules are handled generically below (category
     * specialty, per facility, deps medical_core+scheduling); entries here
     * override the generic rule (e.g. Derma/ObGyn spec_keys, Vet* exclusion).
     */
    const CURATED = [
        // — Registry itself + clinical spine —
        'MedRegistry' => ['category' => 'core',    'installable_per' => 'tenant',   'spec_key' => 'module_registry', 'sort_order' => 1],
        'ClinicCore'  => ['category' => 'medical', 'installable_per' => 'tenant',   'spec_key' => 'medical_core',    'sort_order' => 10],
        // ClinicCore also serves the `scheduling` and `outpatient` spec modules
        // via MedregModule::SPEC_ALIASES (one column, several spec names).

        // — Laboratory family (LabCore engine + ClinicLab bridge) —
        'LabCore'   => ['category' => 'medical', 'installable_per' => 'facility', 'spec_key' => 'laboratory', 'sort_order' => 20],
        'ClinicLab' => ['category' => 'medical', 'installable_per' => 'facility', 'spec_key' => 'laboratory', 'dependencies' => ['medical_core'], 'sort_order' => 21],

        // Lab analyzer integrations (device drivers for LabCore).
        'AbaxisLab' => ['category' => 'integration', 'installable_per' => 'facility', 'dependencies' => ['laboratory'], 'sort_order' => 700],
        'IdexxLab'  => ['category' => 'integration', 'installable_per' => 'facility', 'dependencies' => ['laboratory'], 'sort_order' => 701],
        'NX600Lab'  => ['category' => 'integration', 'installable_per' => 'facility', 'dependencies' => ['laboratory'], 'sort_order' => 702],

        // — Patient-facing / portals (REUSE-MAP §26: PortalApi = patient_portal) —
        'PortalApi'         => ['category' => 'medical', 'installable_per' => 'tenant', 'spec_key' => 'patient_portal', 'dependencies' => ['medical_core'], 'sort_order' => 30],
        'ClinicStaffPortal' => ['category' => 'medical', 'installable_per' => 'tenant', 'dependencies' => ['medical_core'], 'sort_order' => 31],
        'ClinicSite'        => ['category' => 'medical', 'installable_per' => 'tenant', 'dependencies' => ['medical_core'], 'sort_order' => 32],

        // — HR / back office —
        'Essentials' => ['category' => 'core', 'installable_per' => 'tenant', 'spec_key' => 'hr_payroll', 'sort_order' => 40],

        // — Support / integration infrastructure —
        'WhatsApp'       => ['category' => 'support',     'installable_per' => 'tenant', 'sort_order' => 600],
        'ClinicWhatsApp' => ['category' => 'support',     'installable_per' => 'tenant', 'sort_order' => 610],
        'AiCore'         => ['category' => 'support',     'installable_per' => 'tenant', 'sort_order' => 620],
        'SmartStorage'   => ['category' => 'support',     'installable_per' => 'tenant', 'sort_order' => 630],
        'ActivityLog'    => ['category' => 'support',     'installable_per' => 'tenant', 'sort_order' => 640],
        'DemoLogin'      => ['category' => 'support',     'installable_per' => 'tenant', 'sort_order' => 650],
        'NeoSkin'        => ['category' => 'support',     'installable_per' => 'tenant', 'sort_order' => 660],
        'TurboBoost'     => ['category' => 'support',     'installable_per' => 'tenant', 'sort_order' => 670],
        'SaasSwitcher'   => ['category' => 'support',     'installable_per' => 'tenant', 'sort_order' => 680],
        'ReportsModule'  => ['category' => 'support',     'installable_per' => 'tenant', 'sort_order' => 690],
        'Connector'      => ['category' => 'integration', 'installable_per' => 'tenant', 'sort_order' => 710],
        'TPConnector'    => ['category' => 'integration', 'installable_per' => 'tenant', 'sort_order' => 711],

        // — ERP core extensions (non-medical; enabled-by-default semantics) —
        'Crm'                 => ['category' => 'core', 'installable_per' => 'tenant', 'sort_order' => 910],
        'AdvancedTransfer'    => ['category' => 'core', 'installable_per' => 'tenant', 'sort_order' => 920],
        'LandedCost'          => ['category' => 'core', 'installable_per' => 'tenant', 'sort_order' => 921],
        'PurchaseRequisition' => ['category' => 'core', 'installable_per' => 'tenant', 'sort_order' => 922],
        'StockIssues'         => ['category' => 'core', 'installable_per' => 'tenant', 'sort_order' => 923],
        'ProductCompat'       => ['category' => 'core', 'installable_per' => 'tenant', 'sort_order' => 924],
        'Superadmin'          => ['category' => 'core', 'installable_per' => 'tenant', 'sort_order' => 900],

        // — Non-medical verticals: category core + no spec_key marks them
        //   is_medical=false; sorted last (Vet*, Bms, Vrc*) —
        'Bms'            => ['category' => 'core', 'installable_per' => 'tenant', 'sort_order' => 950],
        'Vrc'            => ['category' => 'core', 'installable_per' => 'tenant', 'sort_order' => 951],
        'VrcPortalApi'   => ['category' => 'core', 'installable_per' => 'tenant', 'sort_order' => 952],
        'VetClinic'      => ['category' => 'core', 'installable_per' => 'tenant', 'sort_order' => 960],
        'VetAI'          => ['category' => 'core', 'installable_per' => 'tenant', 'sort_order' => 961],
        'VetAdmission'   => ['category' => 'core', 'installable_per' => 'tenant', 'sort_order' => 962],
        'VetPortal'      => ['category' => 'core', 'installable_per' => 'tenant', 'sort_order' => 963],
        'VetSite'        => ['category' => 'core', 'installable_per' => 'tenant', 'sort_order' => 964],
        'VetStaffPortal' => ['category' => 'core', 'installable_per' => 'tenant', 'sort_order' => 965],
    ];

    /**
     * spec_key overrides for specialty *Clinic modules whose obvious spec id
     * differs from snake_case(name minus "Clinic").
     */
    const SPECIALTY_SPEC_OVERRIDES = [
        'DentalClinic'        => 'dental',
        'DermatologyClinic'   => 'derma',
        'OphthalmologyClinic' => 'ophthalmology',
        'RadiologyClinic'     => 'radiology',
        'PhysiotherapyClinic' => 'physiotherapy',
        'EmergencyClinic'     => 'emergency',
        'GynaecologyClinic'   => 'obgyn',
        'EntClinic'           => 'ent',
        'IvfClinic'           => 'ivf',
        'ServicesClinic'      => 'general_services',
    ];

    /**
     * Example settings schema seeded for DentalClinic — exactly per plan P1-T5.
     */
    const DENTAL_SETTINGS_SCHEMA = [
        'online_booking' => [
            'type'    => 'boolean',
            'label'   => 'Enable online booking',
            'default' => true,
        ],
        'xray_integration' => [
            'type'    => 'select',
            'label'   => 'X-ray integration',
            'options' => ['none' => 'None', 'builtin' => 'Built-in viewer'],
            'default' => 'none',
        ],
    ];

    /**
     * Scan + upsert. Returns a summary: ['synced' => n, 'marked_absent' => n].
     */
    public function sync(): array
    {
        $synced = [];

        foreach (Module::all() as $module) {
            /** @var \Nwidart\Modules\Module $module */
            $name = $module->getName();

            $row = $this->buildRow($name, $module->getLowerName(), $module->getPath(), $module->getDescription());

            MedregModule::updateOrCreate(['module_key' => $name], $row);
            $synced[] = $name;
        }

        // Modules previously catalogued but no longer on disk.
        $absent = 0;
        if (! empty($synced)) {
            $absent = MedregModule::whereNotIn('module_key', $synced)
                ->where('is_present', true)
                ->update(['is_present' => false]);
        }

        return ['synced' => count($synced), 'marked_absent' => $absent];
    }

    /**
     * Build the catalog row for one module: extra.medical block → curated map
     * → generic specialty rule → defaults.
     */
    public function buildRow(string $name, string $alias, string $path, ?string $description = null): array
    {
        $manifest = $this->readManifest($path);
        $medical = isset($manifest['extra']['medical']) && is_array($manifest['extra']['medical'])
            ? $manifest['extra']['medical']
            : [];

        $curated = $this->curatedFor($name);

        $pick = function (string $key, $default = null) use ($medical, $curated) {
            if (array_key_exists($key, $medical)) {
                return $medical[$key];
            }
            if (array_key_exists($key, $curated)) {
                return $curated[$key];
            }

            return $default;
        };

        $category = $pick('category', 'core');
        if (! in_array($category, MedregModule::CATEGORIES, true)) {
            $category = 'core';
        }

        $installablePer = $pick('installable_per', 'tenant');
        if (! in_array($installablePer, MedregModule::INSTALLABLE_PER, true)) {
            $installablePer = 'tenant';
        }

        return [
            'alias'                 => $alias,
            'display_name'          => $pick('display_name', Str::headline($name)),
            'description'           => $pick('description', $description ?: ($manifest['description'] ?? null)),
            'icon'                  => $pick('icon'),
            'category'              => $category,
            'spec_key'              => $pick('spec_key'),
            'dependencies'          => (array) $pick('dependencies', []),
            'optional_dependencies' => (array) $pick('optional_dependencies', []),
            'conflicts_with'        => (array) $pick('conflicts_with', []),
            'installable_per'       => $installablePer,
            'settings_schema'       => $pick('settings_schema'),
            'version'               => $manifest['version'] ?? config($alias . '.module_version'),
            'is_present'            => true,
            'requires_install'      => is_file($path . '/Http/Controllers/InstallController.php'),
            'sort_order'            => (int) $pick('sort_order', 990),
        ];
    }

    /**
     * Curated metadata for a module name, including the generic rule for the
     * 45 *Clinic specialty modules: category specialty, per facility,
     * spec_key = snake_case(name minus "Clinic"), deps medical_core+scheduling.
     */
    public function curatedFor(string $name): array
    {
        if (isset(self::CURATED[$name])) {
            return self::CURATED[$name];
        }

        if (Str::endsWith($name, 'Clinic') && $name !== 'Clinic') {
            $specKey = self::SPECIALTY_SPEC_OVERRIDES[$name]
                ?? Str::snake(Str::beforeLast($name, 'Clinic'));

            $row = [
                'category'        => 'specialty',
                'installable_per' => 'facility',
                'spec_key'        => $specKey,
                'dependencies'    => ['medical_core', 'scheduling'],
                'sort_order'      => 200,
            ];

            if ($name === 'DentalClinic') {
                $row['settings_schema'] = self::DENTAL_SETTINGS_SCHEMA;
            }

            return $row;
        }

        // Unknown module → safe defaults (non-medical, sorted last).
        return ['category' => 'core', 'installable_per' => 'tenant', 'sort_order' => 990];
    }

    protected function readManifest(string $path): array
    {
        $file = rtrim($path, '/\\') . DIRECTORY_SEPARATOR . 'module.json';

        if (! is_file($file)) {
            return [];
        }

        $decoded = json_decode((string) file_get_contents($file), true);

        return is_array($decoded) ? $decoded : [];
    }
}
