<?php

namespace App\Traits;

use App\Enums\MappingOperator;

trait ExportMapCheckRules
{
    protected function getFieldValue($entry, string $path)
    {
        if (str_starts_with($path, '=')) {
            return substr($path, 1);
        }
        if (str_starts_with($path, 'attribute.') && method_exists($entry, 'attributeValues')) {
            return $this->getAttributeValue($entry, substr($path, strlen('attribute.')));
        }
        if (method_exists($entry, 'getAttribute')) {
            $val = $entry->getAttribute($path, true);
            if ($val !== null) {
                return $val;
            }
        }
        if (method_exists($this, 'getCustomAttribute')) {
            $val = $this->getCustomAttribute($entry, $path);
            if ($val !== null) {
                return $val;
            }
        }
        return data_get($entry, $path);
    }

    protected function getAttributeValue($entry, string $slug)
    {
        $values = $entry->attributeValues
            ->where('slug', $slug)
            ->map(fn ($value) => $value->getValue())
            ->filter(fn ($value) => $value !== null && $value !== '')
            ->values();

        if ($values->isEmpty()) {
            return null;
        }

        return $values->count() === 1 ? $values->first() : $values->implode(',');
    }

    protected function castForExport($value)
    {
        if (is_bool($value)) {
            return $value ? 'Y' : 'N';
        }
        if ($value instanceof \DateTimeInterface) {
            return $value->format('Y-m-d');
        }
        if (is_array($value)) {
            return implode(', ', array_map([$this, 'stringify'], $value));
        }
        return $value;
    }

    protected function evaluateCondition($entry, array $rowByField, array $when): bool
    {
        $field = $when['field'] ?? null;
        $operatorString = $when['op'] ?? null;
        $right = $when['value'] ?? null;
        if (!$field) {
            return false;
        }

        $left = array_key_exists((string) $field, $rowByField)
            ? $rowByField[$field]
            : $this->getFieldValue($entry, $field);

        $operator = MappingOperator::fromString($operatorString) ?? MappingOperator::EQ;

        return match ($operator) {
            MappingOperator::EQ => $this->compareString($left, $right) === 0,
            MappingOperator::NEQ => $this->compareString($left, $right) !== 0,

            MappingOperator::IN => in_array(
                $this->toString($left),
                array_map(strval(...), $this->listify($right)),
                true
            ),
            MappingOperator::NOT_IN => !in_array(
                $this->toString($left),
                array_map(strval(...), $this->listify($right)),
                true
            ),

            MappingOperator::CONTAINS => is_string($left) && is_string($right) && mb_stripos($left, $right) !== false,
            MappingOperator::STARTS_WITH => is_string($left) && is_string($right) && mb_stripos($left, $right) === 0,
            MappingOperator::ENDS_WITH => is_string($left) && is_string($right) && $this->endsWithString($left, $right),

            MappingOperator::GT => $this->toNumber($left) > $this->toNumber($right),
            MappingOperator::GTE => $this->toNumber($left) >= $this->toNumber($right),
            MappingOperator::LT => $this->toNumber($left) < $this->toNumber($right),
            MappingOperator::LTE => $this->toNumber($left) <= $this->toNumber($right),

            MappingOperator::IS_EMPTY => $this->isEmptyLike($left),
            MappingOperator::IS_NOT_EMPTY => !$this->isEmptyLike($left),
        };
    }

    protected function listify($value): array
    {
        if (is_array($value)) {
            return $value;
        }
        if ($value === null) {
            return [];
        }
        $parts = preg_split('/[;,]\s*/', (string)$value, -1, PREG_SPLIT_NO_EMPTY);
        return array_map(trim(...), $parts);
    }

    protected function compareString($stringA, $stringB): int
    {
        return strcmp($this->toString($stringA), $this->toString($stringB));
    }

    protected function toString($value): string
    {
        if ($value === null) {
            return '';
        }
        if ($value instanceof \DateTimeInterface) {
            return $value->format('Y-m-d');
        }
        if (is_bool($value)) {
            return $value ? 'Y' : 'N';
        }
        return (string)$value;
    }

    protected function toNumber($value): float
    {
        if ($value === null || $value === '') {
            return 0.0;
        }
        if (is_numeric($value)) {
            return (float)$value;
        }
        return (float)preg_replace('/[^\d.\-]+/', '', (string)$value);
    }

    protected function isEmptyLike($value): bool
    {
        if ($value === null) {
            return true;
        }
        if (is_string($value)) {
            return trim($value) === '';
        }
        if (is_array($value)) {
            return count($value) === 0;
        }
        return false;
    }

    protected function endsWithString(string $haystack, string $needle): bool
    {
        $length = mb_strlen($needle);
        if ($length === 0) {
            return true;
        }
        return mb_strtolower(mb_substr($haystack, -$length)) === mb_strtolower($needle);
    }
}
