<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class BaseModel extends Model
{
    private static ?array $tableColumnsCache = null;

    public function getFillable(): array
    {
        if ($this->isTestingEnv()) {
            $columns = $this->tableColumns();
            if (!empty($columns)) {
                $key = $this->getKeyName();
                return array_values(array_filter($columns, static function ($col) use ($key) {
                    return $col !== $key;
                }));
            }
        }

        $fillable = parent::getFillable();
        if ($this->timestamps) {
            if (!in_array('created_at', $fillable, true)) {
                $fillable[] = 'created_at';
            }
            if (!in_array('updated_at', $fillable, true)) {
                $fillable[] = 'updated_at';
            }
        }

        return $fillable;
    }

    private function isTestingEnv(): bool
    {
        $env = getenv('APP_ENV')
            ?: ($_SERVER['APP_ENV'] ?? ($_ENV['APP_ENV'] ?? null));

        if ($env === 'testing') {
            return true;
        }

        if (defined('PHPUNIT_COMPOSER_INSTALL') || defined('__PHPUNIT_PHAR__')) {
            return true;
        }

        if (function_exists('app')) {
            try {
                $app = app();
                if ($app instanceof \Illuminate\Foundation\Application && $app->runningUnitTests()) {
                    return true;
                }
            } catch (\Throwable $e) {
                // Fallback to env checks when the container isn't booted.
            }
        }

        return false;
    }

    private function tableColumns(): array
    {
        if (self::$tableColumnsCache === null) {
            self::$tableColumnsCache = $this->loadTableColumns();
        }

        return self::$tableColumnsCache[$this->getTable()] ?? [];
    }

    private function loadTableColumns(): array
    {
        $map = [];
        $pattern = null;
        if (function_exists('database_path')) {
            try {
                $pattern = database_path('migrations/*.php');
            } catch (\Throwable $e) {
                $pattern = null;
            }
        }

        if ($pattern === null) {
            $pattern = __DIR__ . '/../../database/migrations/*.php';
        }

        $files = glob($pattern) ?: [];

        foreach ($files as $file) {
            $contents = file_get_contents($file);
            if ($contents === false) {
                continue;
            }

            foreach ($this->parseSqlCreateTables($contents) as $table => $columns) {
                $map[$table] = $columns;
            }

            foreach ($this->parseSchemaCreateTables($contents) as $table => $columns) {
                if (!isset($map[$table]) || empty($map[$table])) {
                    $map[$table] = $columns;
                }
            }
        }

        return $map;
    }

    private function parseSqlCreateTables(string $contents): array
    {
        $map = [];
        if (preg_match_all('/CREATE TABLE `([^`]+)`\\s*\\((.*?)\\)\\s*(?:ENGINE|;)/is', $contents, $matches, PREG_SET_ORDER)) {
            foreach ($matches as $match) {
                $table = $match[1];
                $colsBlock = $match[2];
                preg_match_all('/`([^`]+)`\\s+[^,]+/m', $colsBlock, $colsMatch);
                $columns = $colsMatch[1] ?? [];
                if (!empty($columns)) {
                    $map[$table] = $columns;
                }
            }
        }

        return $map;
    }

    private function parseSchemaCreateTables(string $contents): array
    {
        $map = [];
        if (!preg_match_all('/Schema::create\\(\\s*[\'"]([^\'"]+)[\'"]\\s*,\\s*function\\s*\\([^)]*\\)\\s*\\{(.*?)\\n\\s*\\}\\);/is', $contents, $matches, PREG_SET_ORDER)) {
            return $map;
        }

        foreach ($matches as $match) {
            $table = $match[1];
            $body = $match[2];
            $columns = [];

            if (preg_match_all('/\\$table->\\w+\\(\\s*[\'"]([^\'"]+)[\'"]/', $body, $colMatches)) {
                $columns = array_merge($columns, $colMatches[1]);
            }

            if (str_contains($body, 'timestamps(') || str_contains($body, 'timestamps()')) {
                $columns[] = 'created_at';
                $columns[] = 'updated_at';
            }

            if (str_contains($body, 'softDeletes(') || str_contains($body, 'softDeletes()')) {
                $columns[] = 'deleted_at';
            }

            if (!empty($columns)) {
                $map[$table] = $columns;
            }
        }

        return $map;
    }
}
