<?php

declare(strict_types=1);

namespace BadamSoft\WooProductExporter\Settings;

use function get_option;
use function in_array;
use function is_array;
use function sanitize_key;
use function sanitize_text_field;
use function trailingslashit;
use function update_option;
use function wp_upload_dir;

class SettingsRepository {
    private const OPTION_KEY = 'prodexfo_settings_state';

    /**
     * Get the default settings state structure.
     *
     * @return array<string, array<string, mixed>>
     */
    public function get_default_state(): array {
        return [
            'general'       => [
                'defaultExportFormat' => 'csv',
                'productsPerBatch'    => 1000,
                'exportDirectory'     => 'exports',
                'autoCleanup'         => true,
            ],
            'multilingual'  => [
                'exportMode'      => 'separate_columns',
                'activeLanguages' => [ 'en' ],
            ],
            'database'      => [
                'historyRetention'   => 'all',
                'autoOptimizeWeekly' => false,
            ],
            'notifications' => [
                'exportComplete'    => true,
                'exportError'       => true,
                'scheduledComplete' => true,
                'scheduledError'    => true,
                'storageFull'       => true,
            ],
            'advanced'      => [
                'memoryLimitMb'     => 256,
                'executionTimeout'  => 300,
                'queryCaching'      => true,
            ],
        ];
    }

    /**
     * Retrieve the current settings state merged with defaults.
     *
     * @return array<string, array<string, mixed>>
     */
    public function get_state(): array {
        $raw = get_option( self::OPTION_KEY, [] );

        if ( ! is_array( $raw ) ) {
            $raw = [];
        }

        return $this->mergeWithDefaults( $raw );
    }

    /**
     * Sanitize and persist the settings state.
     *
     * @param array<string, array<string, mixed>> $state
     * @return array<string, array<string, mixed>>
     */
    public function save_state( array $state ): array {
        $sanitized = $this->mergeWithDefaults( $state );
        update_option( self::OPTION_KEY, $sanitized, false );

        return $sanitized;
    }

    private function mergeWithDefaults( array $state ): array {
        $defaults = $this->get_default_state();

        $merged = $defaults;

        $general = isset( $state['general'] ) && is_array( $state['general'] ) ? $state['general'] : [];
        $merged['general'] = [
            'defaultExportFormat' => $this->sanitizeExportFormat( (string) ( $general['defaultExportFormat'] ?? $defaults['general']['defaultExportFormat'] ) ),
            'productsPerBatch'    => $this->sanitizePositiveInt( $general['productsPerBatch'] ?? $defaults['general']['productsPerBatch'], 1000 ),
            'exportDirectory'     => sanitize_text_field( (string) ( $general['exportDirectory'] ?? $defaults['general']['exportDirectory'] ) ),
            'autoCleanup'         => ! empty( $general['autoCleanup'] ),
        ];

        $multilingual = isset( $state['multilingual'] ) && is_array( $state['multilingual'] ) ? $state['multilingual'] : [];
        $merged['multilingual'] = [
            'exportMode'      => $this->sanitizeMultilingualMode( (string) ( $multilingual['exportMode'] ?? $defaults['multilingual']['exportMode'] ) ),
            'activeLanguages' => $this->sanitizeLanguageList( $multilingual['activeLanguages'] ?? $defaults['multilingual']['activeLanguages'] ),
        ];

        $database = isset( $state['database'] ) && is_array( $state['database'] ) ? $state['database'] : [];
        $merged['database'] = [
            'historyRetention'   => $this->sanitizeHistoryRetention( (string) ( $database['historyRetention'] ?? $defaults['database']['historyRetention'] ) ),
            'autoOptimizeWeekly' => ! empty( $database['autoOptimizeWeekly'] ),
        ];

        $notifications = isset( $state['notifications'] ) && is_array( $state['notifications'] ) ? $state['notifications'] : [];
        $merged['notifications'] = [
            'exportComplete'    => ! empty( $notifications['exportComplete'] ),
            'exportError'       => ! empty( $notifications['exportError'] ),
            'scheduledComplete' => ! empty( $notifications['scheduledComplete'] ),
            'scheduledError'    => ! empty( $notifications['scheduledError'] ),
            'storageFull'       => ! empty( $notifications['storageFull'] ),
        ];

        $advanced = isset( $state['advanced'] ) && is_array( $state['advanced'] ) ? $state['advanced'] : [];
        $merged['advanced'] = [
            'memoryLimitMb'    => $this->sanitizePositiveInt( $advanced['memoryLimitMb'] ?? $defaults['advanced']['memoryLimitMb'], 256 ),
            'executionTimeout' => $this->sanitizePositiveInt( $advanced['executionTimeout'] ?? $defaults['advanced']['executionTimeout'], 300 ),
            'queryCaching'     => ! empty( $advanced['queryCaching'] ),
        ];

        return $merged;
    }

    private function sanitizeExportFormat( string $format ): string {
        $format = sanitize_key( $format );

        if ( in_array( $format, [ 'csv', 'xlsx', 'xml', 'json', 'tsv' ], true ) ) {
            return $format;
        }

        return 'csv';
    }

    private function sanitizeMultilingualMode( string $mode ): string {
        $mode = sanitize_key( $mode );

        return in_array( $mode, [ 'separate_columns', 'separate_files' ], true ) ? $mode : 'separate_columns';
    }

    private function sanitizeLanguageList( $languages ): array { // phpcs:ignore
        if ( ! is_array( $languages ) ) {
            return [ 'en' ];
        }

        $sanitized = [];

        foreach ( $languages as $lang ) {
            $key = sanitize_key( (string) $lang );

            if ( '' !== $key ) {
                $sanitized[] = $key;
            }
        }

        if ( empty( $sanitized ) ) {
            $sanitized[] = 'en';
        }

        return $sanitized;
    }

    private function sanitizeHistoryRetention( string $value ): string {
        $value = sanitize_key( $value );

        return in_array( $value, [ 'all', '30', '90', '180', '365' ], true ) ? $value : 'all';
    }

    /**
     * @param mixed $value
     */
    private function sanitizePositiveInt( $value, int $fallback ): int { // phpcs:ignore
        $int = (int) $value;

        if ( $int <= 0 ) {
            $int = $fallback;
        }

        return $int;
    }

    /**
     * Resolve export root directory from settings.general.exportDirectory.
     *
     * Returns an absolute filesystem path with trailing slash.
     *
     * @param string|null $uploads_basedir Optional uploads base directory.
     * @return string
     */
    public function resolve_export_root_directory( ?string $uploads_basedir = null ): string {
        $state = $this->get_state();
        $raw   = (string) ( $state['general']['exportDirectory'] ?? '' );
        $raw   = trim( $raw );

        // Determine fallback base (uploads basedir or uploads base dir).
        $base = $uploads_basedir;

        if ( '' === $base ) {
            $uploads = wp_upload_dir();

            if ( is_array( $uploads ) && ! empty( $uploads['basedir'] ) ) {
                $base = (string) $uploads['basedir'];
            }
        }

        if ( '' === $raw ) {
            return trailingslashit( $base );
        }

        // Absolute path: /var/... or C:\...
        if ( isset( $raw[0] ) && ( '/' === $raw[0] || preg_match( '/^[A-Za-z]:[\\\/]/', $raw ) ) ) {
            return trailingslashit( $raw );
        }

        // Otherwise treat as a relative subdirectory inside uploads.
        $normalized = ltrim( $raw, '/\\' );

        return trailingslashit( trailingslashit( $base ) . $normalized );
    }
}
