<?php

namespace App\Models;

use App\Enums\CSVDelimiter;
use App\Exports\JsonMapExport;
use App\Exports\MapExport;
use Carbon\Carbon;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\App;
use Illuminate\Support\Facades\Config;
use Illuminate\Support\Str;
use Mtc\MercuryDataModels\Traits\EnsuresSlug;
use OwenIt\Auditing\Contracts\Auditable;

class ExportMap extends Model implements Auditable
{
    use HasFactory;
    use EnsuresSlug;
    use \OwenIt\Auditing\Auditable;

    protected $fillable = [
        'name',
        'active',
        'uuid',
        'api_url_enabled',
        'slug',
        'type',
        'conditions',
        'columns',
        'export_time',
        'next_due_at',
        'last_sent_at',
        'last_downloaded_at',
        'export_day_of_week',
        'export_day_of_month',
        'schedule',
        'format',
        'driver',
        'host',
        'port',
        'root',
        'username',
        'password',
        'filename',
        'ssl',
        'schedule_enabled',
        'include_header_row',
        'ftp_export_enabled',
        'mapping_rules',
        'template_id',
        'headers',
        'config',
    ];

    protected $casts = [
        'port' => 'integer',
        'password' => 'encrypted',
        'ssl' => 'boolean',
        'active' => 'boolean',
        'api_url_enabled' => 'boolean',
        'schedule_enabled' => 'boolean',
        'include_header_row' => 'boolean',
        'ftp_export_enabled' => 'boolean',
        'conditions' => 'array',
        'headers' => 'array',
        'mapping_rules' => 'array',
        'columns' => 'array',
        'last_sent_at' => 'datetime:Y-m-d H:i',
        'last_downloaded_at' => 'datetime:Y-m-d H:i',
        'next_due_at' => 'datetime:Y-m-d H:i',
        'config' => 'array',
    ];

    #[\Override]
    protected static function boot(): void
    {
        parent::boot();

        self::creating(static function (self $exportMap) {
            if (empty($exportMap->uuid)) {
                $exportMap->uuid = Str::uuid()->toString();
            }
        });

        self::saving(static function (self $exportMap) {
            $exportMap->ensureSlug();

            if ($exportMap->api_url_enabled && empty($exportMap->uuid)) {
                $exportMap->uuid = Str::uuid()->toString();
            }
        });
    }

    public function getApiUrl(): ?string
    {
        if (!$this->api_url_enabled || empty($this->uuid)) {
            return null;
        }

        return url('/api/exports/' . tenant('id') . '/' . $this->uuid);
    }

    /**
     * Stable sales-channel-history key for this export map's run history. Uses
     * the id (not the mutable name) so renaming the map keeps its history.
     */
    public function salesChannelKey(): string
    {
        return 'export-map-' . $this->id;
    }

    /**
     * Scope to filter by active state
     *
     * @param Builder $query
     * @param $value
     * @return void
     */
    public function scopeActive(Builder $query, $value = null): void
    {
        if (in_array($value, [0, 1], true)) {
            $query->where('active', $value);
        }
    }

    /**
     * Get the next scheduled datetime for the report
     *
     * @param Carbon $from
     * @return mixed
     */
    public function getNextDueAt(Carbon $from): mixed
    {
        return App::make(Config::get('export_maps.schedule_types.' . $this->schedule))
            ->nextTime($from, $this);
    }

    public function exportClass(): string
    {
        return match ($this->format) {
            'csv', 'xlsx' => MapExport::class,
            'json' => JsonMapExport::class,
            default => ''
        };
    }

    public function getJsonEnvelope(): ?string
    {
        $envelope = $this->getConfig('json_envelope');
        return is_string($envelope) && trim($envelope) !== '' ? $envelope : null;
    }

    public function getColumnJsonTypes(): array
    {
        $types = $this->getConfig('column_types', []);
        return is_array($types) ? $types : [];
    }

    public function getColumnJsonPaths(): array
    {
        $paths = $this->getConfig('column_paths', []);
        return is_array($paths) ? $paths : [];
    }

    public function setSchedule(): void
    {
        if ($this->active !== true || $this->schedule_enabled !== true) {
            $this->next_due_at = null;
        }

        if ($this->isDirty('next_due_at')) {
            return;
        }

        $scheduleColumns = [
            'schedule_enabled',
            'schedule',
            'export_time',
            'export_day_of_week',
            'export_day_of_month',
        ];
        if ($this->isDirty($scheduleColumns)) {
            $this->next_due_at = $this->getNextDueAt(Carbon::now());
        }
    }

    public function getConfig(string $key, $default = null)
    {
        return $this->config[$key] ?? $default;
    }

    public function getDelimiter(): string
    {
        if ($this->format !== 'csv') {
            return '';
        }

        $delimiter = $this->getConfig('delimiter');

        if (in_array($delimiter, CSVDelimiter::values(), true)) {
            return $delimiter;
        }

        return config('excel.exports.csv.delimiter', ',');
    }
}
