<?php

namespace App\Console\Commands;

use App\Services\DataverseSchemaService;
use Illuminate\Console\Command;
use Throwable;

/**
 * Idempotently provision the Dataverse schema the cashbook-review page needs to
 * persist the 6 new AutoCount cash/payment document types (AR/AP Payment, AR/AP
 * Refund, AR/AP Deposit) in addition to the existing OR/PV.
 *
 * It adds, on cr6f2_invoice:
 *   • cr6f2_knockoffsjson  (Memo)   — knock-off lines as a JSON array
 *   • cr6f2_depositaccount (String) — deposit (liability) GL account code for deposits
 * and inserts 6 new option values into the existing cr6f2_docuementtype picklist.
 *
 * Default is a dry run; pass --apply to mutate the schema. Mirrors
 * autocount:extend-schema (ExtendAutoCountMasterDataSchema).
 */
class ExtendCashPaymentDocSchema extends Command
{
    protected $signature = 'autocount:extend-cashpayment-schema
                            {--apply : Actually create the columns / option values (default: dry run)}
                            {--solution= : Add the schema to this unmanaged solution (default: Common Data Service Default Solution)}';

    protected $description = 'Add cr6f2_invoice fields + cr6f2_docuementtype options for the 6 new cash/payment document types';

    private const ENTITY = 'cr6f2_invoice';

    /** New cr6f2_docuementtype option values (continuing the 16032000x OR/PV range). */
    private const NEW_DOCTYPE_OPTIONS = [
        160320003 => 'AR Payment',
        160320004 => 'AP Payment',
        160320005 => 'AR Refund',
        160320006 => 'AP Refund',
        160320007 => 'AR Deposit',
        160320008 => 'AP Deposit',
    ];

    public function handle(DataverseSchemaService $schema): int
    {
        $apply    = (bool) $this->option('apply');
        $solution = $this->option('solution') ?: null;

        $this->line('  <fg=cyan>Environment:</> ' . config('dataverse.environment_url'));
        $this->line('  <fg=cyan>Entity:</>      ' . self::ENTITY);
        $this->line('  <fg=cyan>Solution:</>    ' . ($solution ?? '(default)'));
        $this->line('  <fg=cyan>Mode:</>        ' . ($apply ? 'APPLY' : 'dry run'));
        $this->newLine();

        // ── Columns ──
        $columns = [
            'cr6f2_knockoffsjson'  => fn() => $schema->memoAttribute('cr6f2_knockoffsjson', 'Knock-Off Lines (JSON)', 100000, 'AutoCount knock-off lines for payment/refund documents, stored as a JSON array'),
            'cr6f2_depositaccount' => fn() => $schema->stringAttribute('cr6f2_depositaccount', 'Deposit Control Account', 50, 'Deposit (liability) GL account code for A/R and A/P deposit documents'),
            'cr6f2_chequeno'       => fn() => $schema->stringAttribute('cr6f2_chequeno', 'Cheque No', 50, 'Cheque number for A/R and A/P payment / refund documents'),
        ];

        try {
            $existing = array_flip($schema->listAttributeLogicalNames(self::ENTITY));
        } catch (Throwable $e) {
            $this->error('  ✗ Could not read existing attributes: ' . $e->getMessage());
            return Command::FAILURE;
        }

        $this->line('<fg=yellow>▶</> Columns');
        $created = $skipped = 0;
        foreach ($columns as $logical => $bodyBuilder) {
            if (isset($existing[strtolower($logical)])) {
                $this->line("  <fg=gray>·</> {$logical} <fg=gray>(already exists, skipped)</>");
                $skipped++;
                continue;
            }
            if (!$apply) {
                $this->line("  <fg=green>+</> {$logical} <fg=gray>(would create)</>");
                continue;
            }
            try {
                $schema->createAttribute(self::ENTITY, $bodyBuilder(), $solution);
                $this->line("  <fg=green>✓</> {$logical} <fg=green>created</>");
                $created++;
            } catch (Throwable $e) {
                return $this->reportFailure($logical, $e);
            }
        }
        $this->newLine();

        // ── Option values on cr6f2_docuementtype ──
        $this->line('<fg=yellow>▶</> cr6f2_docuementtype options');
        try {
            $existingOpts = $schema->listPicklistOptions(self::ENTITY, 'cr6f2_docuementtype');
        } catch (Throwable $e) {
            $this->error('  ✗ Could not read existing option values: ' . $e->getMessage());
            return Command::FAILURE;
        }

        $optCreated = $optSkipped = 0;
        foreach (self::NEW_DOCTYPE_OPTIONS as $value => $label) {
            if (array_key_exists($value, $existingOpts)) {
                $this->line("  <fg=gray>·</> {$value} {$label} <fg=gray>(already exists, skipped)</>");
                $optSkipped++;
                continue;
            }
            if (!$apply) {
                $this->line("  <fg=green>+</> {$value} {$label} <fg=gray>(would insert)</>");
                continue;
            }
            try {
                $schema->insertOptionValue(self::ENTITY, 'cr6f2_docuementtype', $value, $label, $solution);
                $this->line("  <fg=green>✓</> {$value} {$label} <fg=green>inserted</>");
                $optCreated++;
            } catch (Throwable $e) {
                return $this->reportFailure("docuementtype option {$value}", $e);
            }
        }
        $this->newLine();

        if ($apply) {
            $this->info("Done. Columns: created {$created}, skipped {$skipped}. Options: inserted {$optCreated}, skipped {$optSkipped}.");
            $this->line('Mirror the changes into <fg=yellow>ref/OCR_Invoice_*/Entities/cr6f2_Invoice/Entity.xml</> on the next solution export.');
        } else {
            $this->info('Dry run complete. Re-run with <fg=yellow>--apply</> to mutate the schema.');
        }

        return Command::SUCCESS;
    }

    private function reportFailure(string $what, Throwable $e): int
    {
        $m = $e->getMessage();
        $isPerm = str_contains($m, '[403]')
            || str_contains($m, 'prvCreateAttribute')
            || str_contains($m, '0x80040220')
            || str_contains($m, 'Principal user is missing');

        if ($isPerm) {
            $this->newLine();
            $this->error('  ✗ Schema mutation denied — service principal is missing the System Customizer role.');
            $this->line('    Service principal: <fg=yellow>' . config('dataverse.client_id') . '</>');
            $this->line('    Grant in Power Platform Admin Center → Environments → Settings →');
            $this->line('    Users + permissions → Application users.');
            $this->line('    Raw error: ' . $m);
        } else {
            $this->error("  ✗ {$what}: " . $m);
        }
        return Command::FAILURE;
    }
}
