<?php

namespace App\Services\Care;

use App\Models\Organization;

/**
 * Org-level price lists for GP (general practice) and specialty module services.
 * Drug prices stay on Inventory (care_drugs.unit_price_minor); lab prices on investigation types.
 */
class CarePricingService
{
    /**
     * @return list<array{code: string, label: string, amount_minor: int, type: string}>
     */
    public function defaultGpServices(): array
    {
        return [
            [
                'code' => 'gp.consultation',
                'label' => 'GP consultation',
                'amount_minor' => (int) config('care.billing.consultation_fee_minor', 5000),
                'type' => 'consultation',
            ],
            [
                'code' => 'gp.follow_up',
                'label' => 'Follow-up consultation',
                'amount_minor' => (int) config('care.billing.follow_up_fee_minor', 3000),
                'type' => 'consultation',
            ],
            [
                'code' => 'gp.patient_card',
                'label' => 'Patient card / registration',
                'amount_minor' => (int) config('care.billing.patient_card_fee_minor', 2000),
                'type' => 'misc',
            ],
            [
                'code' => 'gp.vitals',
                'label' => 'Vitals / nursing charge',
                'amount_minor' => (int) config('care.billing.vitals_fee_minor', 1000),
                'type' => 'nursing',
            ],
            [
                'code' => 'gp.procedure',
                'label' => 'Minor procedure',
                'amount_minor' => (int) config('care.billing.minor_procedure_fee_minor', 5000),
                'type' => 'procedure',
            ],
            [
                'code' => 'gp.home_visit',
                'label' => 'Home visit',
                'amount_minor' => (int) config('care.billing.home_visit_fee_minor', 15000),
                'type' => 'consultation',
            ],
        ];
    }

    /**
     * @return list<array{code: string, label: string, amount_minor: int, type: string}>
     */
    public function gpServices(Organization $organization): array
    {
        $catalog = collect($this->defaultGpServices())->keyBy(fn ($s) => (string) $s['code']);
        $stored = data_get($organization->settings, 'gp_services');
        if (is_array($stored)) {
            foreach ($stored as $service) {
                $code = (string) ($service['code'] ?? '');
                if ($code === '' || ! $catalog->has($code)) {
                    continue;
                }
                $prior = $catalog->get($code, []);
                $catalog->put($code, array_merge(is_array($prior) ? $prior : [], [
                    'code' => $code,
                    'label' => (string) ($service['label'] ?? $prior['label'] ?? $code),
                    'amount_minor' => (int) ($service['amount_minor'] ?? $prior['amount_minor'] ?? 0),
                    'type' => (string) ($service['type'] ?? $prior['type'] ?? 'misc'),
                ]));
            }
        }

        return $catalog->values()->all();
    }

    public function gpAmount(Organization $organization, string $code): int
    {
        $match = collect($this->gpServices($organization))
            ->first(fn ($s) => ($s['code'] ?? '') === $code);

        return (int) ($match['amount_minor'] ?? 0);
    }

    public function consultationFee(Organization $organization): int
    {
        return $this->gpAmount($organization, 'gp.consultation');
    }

    public function patientCardFee(Organization $organization): int
    {
        return $this->gpAmount($organization, 'gp.patient_card');
    }

    /**
     * @param  list<array{code: string, amount_minor: int|string, label?: string}>  $rows
     * @return list<array{code: string, label: string, amount_minor: int, type: string}>
     */
    public function updateGpServices(Organization $organization, array $rows): array
    {
        $defaults = collect($this->defaultGpServices())->keyBy(fn ($s) => (string) $s['code']);
        $merged = [];

        foreach ($rows as $row) {
            $code = (string) ($row['code'] ?? '');
            if ($code === '' || ! $defaults->has($code)) {
                continue;
            }
            $base = $defaults->get($code);
            $merged[] = [
                'code' => $code,
                'label' => (string) ($base['label'] ?? $code),
                'amount_minor' => max(0, (int) round(((float) ($row['amount'] ?? 0)) * 100)),
                'type' => (string) ($base['type'] ?? 'misc'),
            ];
        }

        // Keep any defaults not submitted.
        $byCode = collect($merged)->keyBy('code');
        foreach ($defaults as $code => $base) {
            if (! $byCode->has($code)) {
                $byCode->put($code, $base);
            }
        }

        $list = $byCode->values()->all();
        $settings = $organization->settings ?? [];
        $settings['gp_services'] = $list;
        $organization->update(['settings' => $settings]);

        return $list;
    }

    /**
     * Persist specialty module service amounts (major currency input → minor).
     *
     * @param  list<array{code: string, amount?: float|int|string, label?: string}>  $rows
     * @return list<array{code: string, label: string, amount_minor: int, type: string}>
     */
    public function updateSpecialtyServices(
        Organization $organization,
        string $moduleKey,
        array $rows,
        SpecialtyShellService $shell,
    ): array {
        $current = collect($shell->provisionedServices($organization, $moduleKey))
            ->keyBy(fn ($s) => (string) ($s['code'] ?? ''));

        foreach ($rows as $row) {
            $code = (string) ($row['code'] ?? '');
            if ($code === '' || ! $current->has($code)) {
                continue;
            }
            $prior = $current->get($code);
            $current->put($code, array_merge(is_array($prior) ? $prior : [], [
                'code' => $code,
                'label' => (string) ($row['label'] ?? $prior['label'] ?? $code),
                'amount_minor' => max(0, (int) round(((float) ($row['amount'] ?? 0)) * 100)),
                'type' => (string) ($prior['type'] ?? 'misc'),
            ]));
        }

        $list = $current->filter(fn ($s, $code) => $code !== '')->values()->all();
        $settings = $organization->settings ?? [];
        $provisioning = is_array($settings['specialty_module_provisioning'] ?? null)
            ? $settings['specialty_module_provisioning']
            : [];
        $record = is_array($provisioning[$moduleKey] ?? null) ? $provisioning[$moduleKey] : [];
        $record['services'] = $list;
        $provisioning[$moduleKey] = $record;
        $settings['specialty_module_provisioning'] = $provisioning;
        $organization->update(['settings' => $settings]);

        return $list;
    }
}
