<?php
/**
 * Configuration Form Model for Caterpoint Module
 *
 * @category    PortalPeople
 * @package     Caterpoint
 * @author      Portal People Development Team
 * @copyright   Copyright (c) 2026 Portal People (https://portalpeople.co.uk)
 */

namespace PortalPeople\caterpoint\models;

use Yii;
use yii\base\Model;
use backend\components\Security;
use PortalPeople\caterpoint\components\CaterpointClient;

/**
 * Configuration Form Model
 *
 * Handles Caterpoint module configuration form data and persistence.
 */
class ConfigForm extends Model
{
    public $enabled = true;
    public $apiEndpoint = '';
    public $apiToken = '';
    public $requestTimeout = 30;
    public $pollLimit = 100;
    public $orderNumberPrefix = 'CP-';
    public $autoMarkExported = false;
    public $debug = false;
    public $statusMap = '';

    /**
     * @inheritdoc
     */
    public function rules()
    {
        return [
            [['enabled', 'debug', 'autoMarkExported'], 'boolean'],
            [['apiEndpoint'], 'required', 'when' => function ($model) {
                return (bool) $model->enabled;
            }, 'whenClient' => "function () { return $('#configform-enabled').is(':checked'); }"],
            [['apiToken'], 'required', 'when' => function ($model) {
                return (bool) $model->enabled;
            }, 'whenClient' => "function () { return $('#configform-enabled').is(':checked'); }"],
            [['apiEndpoint'], 'url'],
            [['apiEndpoint', 'apiToken', 'orderNumberPrefix', 'statusMap'], 'string'],
            [['apiEndpoint'], 'string', 'max' => 255],
            [['apiToken'], 'string', 'max' => 1000],
            [['orderNumberPrefix'], 'string', 'max' => 20],
            [['requestTimeout'], 'integer', 'min' => 5, 'max' => 300],
            [['pollLimit'], 'integer', 'min' => 1, 'max' => 500],
            [['statusMap'], 'validateStatusMap'],
            [['apiEndpoint'], 'filter', 'filter' => 'trim'],
            [['apiToken'], 'filter', 'filter' => 'trim'],
            [['orderNumberPrefix'], 'filter', 'filter' => 'trim'],
        ];
    }

    /**
     * @inheritdoc
     */
    public function attributeLabels()
    {
        return [
            'enabled' => 'Enable Caterpoint Integration',
            'apiEndpoint' => 'Caterpoint API Endpoint',
            'apiToken' => 'API Bearer Token',
            'requestTimeout' => 'Request Timeout (seconds)',
            'pollLimit' => 'Import Batch Size',
            'orderNumberPrefix' => 'Order Number Prefix',
            'autoMarkExported' => 'Mark Orders Exported After Import',
            'debug' => 'Enable Debug Logging',
            'statusMap' => 'Status Mapping (JSON)',
        ];
    }

    /**
     * @inheritdoc
     */
    public function attributeHints()
    {
        return [
            'apiEndpoint' => 'Usually https://cp3-bayley-test.caterpoint.co.uk/api',
            'apiToken' => 'Token used in Authorization: Bearer <token>',
            'requestTimeout' => 'HTTP timeout for API requests',
            'pollLimit' => 'Max orders fetched per import call',
            'orderNumberPrefix' => 'Prefix added when generating local order numbers',
            'autoMarkExported' => 'If enabled, imported orders are marked exported in Caterpoint',
            'statusMap' => 'Optional JSON map from Caterpoint status => local status',
        ];
    }

    /**
     * Validate status mapping JSON payload.
     *
     * @param string $attribute
     */
    public function validateStatusMap($attribute)
    {
        if ($this->$attribute === null || trim((string) $this->$attribute) === '') {
            return;
        }

        $decoded = json_decode((string) $this->$attribute, true);
        if (json_last_error() !== JSON_ERROR_NONE) {
            $this->addError($attribute, 'Invalid JSON format: ' . json_last_error_msg());
            return;
        }

        if (!is_array($decoded)) {
            $this->addError($attribute, 'Status map must decode to an object/associative array.');
            return;
        }

        foreach ($decoded as $key => $value) {
            if (!is_string($key) || $key === '') {
                $this->addError($attribute, 'All status map keys must be non-empty strings.');
                return;
            }
            if (!is_scalar($value)) {
                $this->addError($attribute, 'All status map values must be scalar values.');
                return;
            }
        }
    }

    /**
     * Load configuration from params.
     *
     * @return bool
     */
    public function loadFromParams(): bool
    {
        $params = Yii::$app->params;
        $config = $params['plugins']['caterpoint'] ?? $params['caterpoint'] ?? null;

        if (!is_array($config)) {
            return false;
        }

        $this->enabled = isset($config['enabled']) ? (bool) $config['enabled'] : true;
        $this->apiEndpoint = (string) ($config['apiEndpoint'] ?? '');
        $this->apiToken = $this->decryptSensitiveData((string) ($config['apiToken'] ?? ''));
        $this->requestTimeout = (int) ($config['requestTimeout'] ?? 30);
        $this->pollLimit = (int) ($config['pollLimit'] ?? 100);
        $this->orderNumberPrefix = (string) ($config['orderNumberPrefix'] ?? 'CP-');
        $this->autoMarkExported = (bool) ($config['autoMarkExported'] ?? false);
        $this->debug = (bool) ($config['debug'] ?? false);

        $statusMap = $config['statusMap'] ?? $this->getDefaultStatusMapping();
        if (is_array($statusMap)) {
            $this->statusMap = json_encode($statusMap, JSON_PRETTY_PRINT);
        } elseif (is_string($statusMap)) {
            $decoded = json_decode($statusMap, true);
            $this->statusMap = is_array($decoded)
                ? json_encode($decoded, JSON_PRETTY_PRINT)
                : $statusMap;
        }

        return true;
    }

    /**
     * Save configuration to params-local.php.
     *
     * @return bool
     */
    public function saveToParams(): bool
    {
        try {
            $paramsFile = $this->resolveParamsFile();

            $params = [];
            if (file_exists($paramsFile)) {
                $params = include $paramsFile;
                if (!is_array($params)) {
                    $params = [];
                }
            }

            if (!isset($params['plugins']) || !is_array($params['plugins'])) {
                $params['plugins'] = [];
            }

            $config = [
                'enabled' => (bool) $this->enabled,
                'apiEndpoint' => rtrim((string) $this->apiEndpoint, '/'),
                'apiToken' => $this->encryptSensitiveData((string) $this->apiToken),
                'requestTimeout' => (int) $this->requestTimeout,
                'pollLimit' => (int) $this->pollLimit,
                'orderNumberPrefix' => (string) $this->orderNumberPrefix,
                'autoMarkExported' => (bool) $this->autoMarkExported,
                'debug' => (bool) $this->debug,
                'statusMap' => $this->getStatusMapArray(),
            ];

            $params['plugins']['caterpoint'] = $config;
            $params['caterpoint'] = $config;

            $dir = dirname($paramsFile);
            if (!is_dir($dir)) {
                @mkdir($dir, 0755, true);
            }

            $content = "<?php\n\nreturn " . var_export($params, true) . ";\n";
            return file_put_contents($paramsFile, $content) !== false;
        } catch (\Throwable $throwable) {
            Yii::error('Failed to save Caterpoint configuration: ' . $throwable->getMessage(), __METHOD__);
            return false;
        }
    }

    /**
     * Test Caterpoint API connection.
     *
     * @return array
     */
    public function testConnection(): array
    {
        try {
            $client = new CaterpointClient([
                'apiEndpoint' => (string) $this->apiEndpoint,
                'apiToken' => (string) $this->apiToken,
                'timeout' => (int) $this->requestTimeout,
            ]);

            $result = $client->getOrders([], 1, 0);
            $orders = isset($result['orders']) && is_array($result['orders']) ? $result['orders'] : [];

            return [
                'success' => true,
                'message' => 'Connected to Caterpoint API successfully.',
                'details' => [
                    'endpoint' => (string) $this->apiEndpoint,
                    'sampleCount' => count($orders),
                ],
            ];
        } catch (\Throwable $throwable) {
            return [
                'success' => false,
                'message' => 'Connection failed: ' . $throwable->getMessage(),
            ];
        }
    }

    /**
     * Reset Caterpoint configuration to disabled defaults.
     *
     * @return bool
     */
    public function resetToDefaults(): bool
    {
        try {
            $paramsFile = $this->resolveParamsFile();

            $params = [];
            if (file_exists($paramsFile)) {
                $params = include $paramsFile;
                if (!is_array($params)) {
                    $params = [];
                }
            }

            if (!isset($params['plugins']) || !is_array($params['plugins'])) {
                $params['plugins'] = [];
            }

            $params['plugins']['caterpoint'] = ['enabled' => false];
            $params['caterpoint'] = ['enabled' => false];

            $content = "<?php\n\nreturn " . var_export($params, true) . ";\n";
            return file_put_contents($paramsFile, $content) !== false;
        } catch (\Throwable $throwable) {
            Yii::error('Failed to reset Caterpoint configuration: ' . $throwable->getMessage(), __METHOD__);
            return false;
        }
    }

    /**
     * Export the current form as array.
     *
     * @return array
     */
    public function exportConfig(): array
    {
        return [
            'enabled' => (bool) $this->enabled,
            'apiEndpoint' => (string) $this->apiEndpoint,
            'apiToken' => $this->apiToken !== '' ? '***masked***' : '',
            'requestTimeout' => (int) $this->requestTimeout,
            'pollLimit' => (int) $this->pollLimit,
            'orderNumberPrefix' => (string) $this->orderNumberPrefix,
            'autoMarkExported' => (bool) $this->autoMarkExported,
            'debug' => (bool) $this->debug,
            'statusMap' => $this->getStatusMapArray(),
        ];
    }

    /**
     * @return array
     */
    public function getDefaultStatusMapping(): array
    {
        return [
            'pending' => 'Pending',
            'processing' => 'Processing',
            'confirmed' => 'Confirmed',
            'dispatched' => 'Dispatched',
            'delivered' => 'Delivered',
            'cancelled' => 'Cancelled',
        ];
    }

    /**
     * @return array
     */
    private function getStatusMapArray(): array
    {
        if ($this->statusMap === null || trim((string) $this->statusMap) === '') {
            return $this->getDefaultStatusMapping();
        }

        $decoded = json_decode((string) $this->statusMap, true);
        if (is_array($decoded)) {
            return $decoded;
        }

        return $this->getDefaultStatusMapping();
    }

    /**
     * @return string
     */
    private function resolveParamsFile(): string
    {
        $aliases = [
            '@backend/config/params-local.php',
            '@common/config/params-local.php',
            '@frontend/config/params-local.php',
        ];

        $resolved = [];
        foreach ($aliases as $alias) {
            try {
                $resolved[] = Yii::getAlias($alias);
            } catch (\Throwable $throwable) {
            }
        }

        foreach ($resolved as $filePath) {
            if (file_exists($filePath)) {
                return $filePath;
            }
        }

        if (!empty($resolved)) {
            return $resolved[0];
        }

        throw new \RuntimeException('Cannot resolve params-local.php path for Caterpoint configuration.');
    }

    /**
     * @param string $value
     * @return string
     */
    private function encryptSensitiveData(string $value): string
    {
        if ($value === '') {
            return '';
        }

        if (class_exists(Security::class)) {
            try {
                return Security::encryptSensitiveData($value);
            } catch (\Throwable $throwable) {
            }
        }

        return $value;
    }

    /**
     * @param string $value
     * @return string
     */
    private function decryptSensitiveData(string $value): string
    {
        if ($value === '') {
            return '';
        }

        if (class_exists(Security::class)) {
            try {
                return Security::decryptSensitiveData($value);
            } catch (\Throwable $throwable) {
            }
        }

        return $value;
    }
}
