<?php

namespace Modules\MedOrganization\Database\Seeders;

use Illuminate\Database\Seeder;
use Illuminate\Support\Facades\DB;
use Spatie\Permission\Models\Permission;
use Spatie\Permission\Models\Role;
use Modules\MedOrganization\Entities\OrgAccessRule;

/**
 * Seeds the predefined medical roles per business (spec §4 predefined_roles;
 * TENANCY-PERMISSIONS-AUDIT §2/§3 — seed, don't rewrite):
 *
 *  - super_admin is SKIPPED (= the existing superadmin user_type).
 *  - patient is SKIPPED (= portal-only owner, never an ERP web-guard role).
 *  - the other 12 are created as `{key}#{business_id}` (guard web,
 *    is_default 0) ONLY when missing; existing roles are never modified.
 *  - each new role gets a conservative starter permission set drawn from
 *    permissions that ALREADY exist in the permissions table (verified at
 *    runtime; unknown names are silently dropped) plus fitting medorg.* ones.
 *
 * Also seeds the 4 spec ABAC examples into org_access_rules (idempotent).
 *
 * Run via `php artisan medorg:seed-roles {business_id?}` or module:seed.
 */
class OrgRolesSeeder extends Seeder
{
    /** The 12 ERP-side predefined roles → starter permission sets. */
    public const ROLE_PERMISSIONS = [
        'facility_admin' => [
            'medorg.facility.view', 'medorg.facility.manage', 'medorg.department.manage',
            'medorg.room.manage', 'medorg.license.view', 'medorg.license.manage',
            'medorg.license.verify', 'medorg.access_rules.manage', 'medorg.access_all_departments',
            'user.view',
        ],
        'department_head' => [
            'medorg.facility.view', 'medorg.room.manage', 'medorg.license.view',
            'clinic.access_module', 'clinic.view_appointments',
            'essentials.approve_leave',
        ],
        'doctor' => [
            'medorg.license.view',
            'clinic.access_module', 'clinic.view_appointments', 'clinic.manage_appointments',
            'clinic.manage_prescriptions', 'clinic.record_exam_findings', 'clinic.view_exam_findings',
            'customer.view',
        ],
        'nurse' => [
            'medorg.license.view',
            'clinic.access_module', 'clinic.view_appointments', 'clinic.record_exam_findings',
            'customer.view',
        ],
        'pharmacist' => [
            'medorg.license.view',
            'clinic.access_module', 'clinic.manage_medicines',
            'customer.view', 'product.view',
        ],
        'lab_technician' => [
            'medorg.license.view',
            'customer.view',
        ],
        'radiologist' => [
            'medorg.license.view',
            'clinic.access_module', 'clinic.view_appointments',
            'customer.view',
        ],
        'receptionist' => [
            'customer.view', 'customer.create', 'customer.update',
            'clinic.access_module', 'clinic.view_appointments', 'clinic.manage_appointments',
            'sell.view',
        ],
        'billing_clerk' => [
            'sell.view', 'sell.create',
            'customer.view',
        ],
        'accountant' => [
            'account.access', 'expense.access',
            'sell.view', 'purchase.view',
            'purchase_n_sell_report.view', 'expense_report.view', 'tax_report.view', 'contacts_report.view',
        ],
        'hr_manager' => [
            'user.view',
            'essentials.crud_all_leave', 'essentials.approve_leave', 'essentials.crud_leave_type',
            'essentials.crud_all_attendance', 'essentials.crud_department', 'essentials.crud_designation',
            'essentials.view_all_payroll', 'essentials.create_payroll',
        ],
        'inventory_manager' => [
            'product.view', 'product.create', 'product.update',
            'purchase.view', 'purchase.create', 'purchase.update',
            'stock_report.view', 'brand.view', 'unit.view', 'category.view',
        ],
    ];

    /** Runs for every business when invoked via module:seed. */
    public function run(): void
    {
        $businessIds = DB::table('business')->pluck('id')->all();

        foreach ($businessIds as $businessId) {
            $this->seedForBusiness((int) $businessId);
        }
    }

    /**
     * Idempotently ensure the 12 roles + example ABAC rules for one business.
     *
     * @return string[] role keys ensured
     */
    public function seedForBusiness(int $businessId): array
    {
        $ensured = [];

        // Only assign permission names that actually exist (brief: verify
        // against the permissions table; when unsure assign none).
        $wanted = collect(self::ROLE_PERMISSIONS)->flatten()->unique()->values()->all();
        $existing = Permission::whereIn('name', $wanted)
            ->where('guard_name', 'web')
            ->pluck('name')
            ->all();

        foreach (self::ROLE_PERMISSIONS as $key => $permissions) {
            $name = $key . '#' . $businessId;

            $role = Role::where('name', $name)->where('guard_name', 'web')->first();
            if (! $role) {
                $role = Role::create([
                    'name'        => $name,
                    'business_id' => $businessId,
                    'guard_name'  => 'web',
                    'is_default'  => 0,
                ]);

                $grant = array_values(array_intersect($permissions, $existing));
                if (! empty($grant)) {
                    try {
                        $role->syncPermissions($grant);
                    } catch (\Throwable $e) {
                        \Log::warning("MedOrganization: could not grant starter permissions to {$name}: " . $e->getMessage());
                    }
                }
            }

            $ensured[] = $key;
        }

        $this->seedAccessRules($businessId);

        return $ensured;
    }

    /**
     * The 4 ABAC examples from Specs §4, expressed as org_access_rules rows
     * (evaluated by Services\MedAccess). Idempotent via updateOrCreate.
     */
    protected function seedAccessRules(int $businessId): void
    {
        $rules = [
            // "Doctor can only view patients assigned to them"
            ['role_key' => 'doctor', 'entity' => 'patient', 'rule_key' => 'only_assigned',
             'effect' => 'allow', 'rule_value' => ['abilities' => ['view']]],

            // "Nurse can only modify vitals, not diagnosis" (field groups, D6)
            ['role_key' => 'nurse', 'entity' => '*', 'rule_key' => 'field_group',
             'effect' => 'allow', 'rule_value' => ['groups' => ['vitals']]],
            ['role_key' => 'nurse', 'entity' => '*', 'rule_key' => 'field_group',
             'effect' => 'deny', 'rule_value' => ['groups' => ['assessment']]],

            // "Billing clerk cannot view clinical notes"
            ['role_key' => 'billing_clerk', 'entity' => 'clinical_note', 'rule_key' => 'entity_ability',
             'effect' => 'deny', 'rule_value' => ['ability' => 'view']],

            // "Pharmacist can view prescriptions but not edit diagnoses"
            ['role_key' => 'pharmacist', 'entity' => 'prescription', 'rule_key' => 'entity_ability',
             'effect' => 'allow', 'rule_value' => ['ability' => 'view']],
            ['role_key' => 'pharmacist', 'entity' => 'diagnosis', 'rule_key' => 'entity_ability',
             'effect' => 'deny', 'rule_value' => ['ability' => 'edit']],
        ];

        foreach ($rules as $rule) {
            OrgAccessRule::updateOrCreate(
                [
                    'business_id' => $businessId,
                    'role_key'    => $rule['role_key'],
                    'entity'      => $rule['entity'],
                    'rule_key'    => $rule['rule_key'],
                    'effect'      => $rule['effect'],
                ],
                [
                    'role_id'    => null,
                    'rule_value' => $rule['rule_value'],
                    'is_active'  => true,
                ]
            );
        }
    }
}
