<?php
/**
 * SalonPro X - Base Model Class
 * All models should extend this class for CRUD operations
 */

namespace core;

class Model
{
    protected string $table = '';
    protected string $primaryKey = 'id';
    protected bool $timestamps = true;
    protected array $fillable = [];
    protected array $guarded = ['id'];
    protected array $hidden = [];
    protected array $casts = [];
    protected array $rules = [];
    protected array $relations = [];
    protected ?object $db = null;

    public function __construct()
    {
        $this->db = Database::getInstance();
    }

    /**
     * Get all records
     */
    public function all(): array
    {
        return $this->db->table($this->table)
            ->where('deleted_at', 'IS', null)
            ->orderBy($this->primaryKey, 'DESC')
            ->get();
    }

    /**
     * Find record by primary key
     */
    public function find(mixed $id): ?object
    {
        $result = $this->db->table($this->table)->find($id);
        if ($result) {
            $this->castAttributes($result);
        }
        return $result;
    }

    /**
     * Find by custom field
     */
    public function findBy(string $field, mixed $value): ?object
    {
        $result = $this->db->table($this->table)->where($field, $value)->first();
        if ($result) {
            $this->castAttributes($result);
        }
        return $result;
    }

    /**
     * Find all matching criteria
     */
    public function findAllBy(string $field, mixed $value): array
    {
        return $this->db->table($this->table)->where($field, $value)->get();
    }

    /**
     * Create a new record
     */
    public function create(array $data): int
    {
        $data = $this->filterFillable($data);
        if ($this->timestamps) {
            $data['created_at'] = date(DATE_TIME_FORMAT);
            $data['updated_at'] = date(DATE_TIME_FORMAT);
        }
        return $this->db->table($this->table)->insert($data);
    }

    /**
     * Update a record
     */
    public function update(mixed $id, array $data): int
    {
        $data = $this->filterFillable($data);
        if ($this->timestamps) {
            $data['updated_at'] = date(DATE_TIME_FORMAT);
        }
        return $this->db->table($this->table)
            ->where($this->primaryKey, $id)
            ->update($data);
    }

    /**
     * Delete a record (soft delete if supported)
     */
    public function delete(mixed $id): int
    {
        Auth::logAudit('delete', $this->table, $id, null, null);
        return $this->db->table($this->table)
            ->where($this->primaryKey, $id)
            ->delete();
    }

    /**
     * Soft delete
     */
    public function softDelete(mixed $id): int
    {
        $data = ['deleted_at' => date(DATE_TIME_FORMAT)];
        return $this->db->table($this->table)
            ->where($this->primaryKey, $id)
            ->update($data);
    }

    /**
     * Paginate results
     */
    public function paginate(int $perPage = PAGINATION_LIMIT, int $page = 1): object
    {
        return $this->db->table($this->table)
            ->orderBy($this->primaryKey, 'DESC')
            ->paginate($perPage, $page);
    }

    /**
     * Get count of records
     */
    public function count(): int
    {
        return $this->db->table($this->table)->count();
    }

    /**
     * Get query builder instance
     */
    public function query(): object
    {
        return $this->db->table($this->table);
    }

    /**
     * Validate data against model rules
     */
    public function validate(array $data, array $customRules = []): bool
    {
        $rules = array_merge($this->rules, $customRules);
        if (empty($rules)) {
            return true;
        }

        $validator = Validator::make($data, $rules);
        return $validator->passes();
    }

    /**
     * Get validation errors
     */
    public function errors(): array
    {
        return [];
    }

    /**
     * Filter data to only fillable fields
     */
    protected function filterFillable(array $data): array
    {
        if (!empty($this->fillable)) {
            $filtered = [];
            foreach ($this->fillable as $field) {
                if (isset($data[$field])) {
                    $filtered[$field] = $data[$field];
                }
            }
            return $filtered;
        }

        if (!empty($this->guarded)) {
            foreach ($this->guarded as $field) {
                unset($data[$field]);
            }
        }

        return $data;
    }

    /**
     * Cast attribute types
     */
    protected function castAttributes(object &$record): void
    {
        foreach ($this->casts as $attribute => $type) {
            if (!isset($record->$attribute)) {
                continue;
            }

            $record->$attribute = match ($type) {
                'int', 'integer' => (int)$record->$attribute,
                'float', 'double', 'decimal' => (float)$record->$attribute,
                'bool', 'boolean' => (bool)$record->$attribute,
                'array', 'json' => json_decode($record->$attribute ?? '{}', true) ?? [],
                'object' => json_decode($record->$attribute ?? '{}'),
                'date' => $record->$attribute ? date(DATE_DISPLAY_FORMAT, strtotime($record->$attribute)) : null,
                'datetime' => $record->$attribute ? date(DATE_TIME_DISPLAY_FORMAT, strtotime($record->$attribute)) : null,
                default => $record->$attribute,
            };
        }
    }

    /**
     * Get table name
     */
    public function getTable(): string
    {
        return $this->table;
    }

    /**
     * Get primary key name
     */
    public function getPrimaryKey(): string
    {
        return $this->primaryKey;
    }

    /**
     * Generate a unique slug
     */
    public function generateSlug(string $name, ?int $excludeId = null): string
    {
        $slug = strtolower(trim(preg_replace('/[^a-zA-Z0-9]+/', '-', $name), '-'));

        $db = Database::getInstance();
        $db->table($this->table)->where('slug', 'LIKE', $slug . '%');

        if ($excludeId) {
            $db->where($this->primaryKey, '!=', $excludeId);
        }

        $existing = $db->get();
        if (count($existing) > 0) {
            $slug .= '-' . count($existing);
        }

        return $slug;
    }

    /**
     * Generate unique code/number
     */
    public function generateCode(string $prefix = '', int $length = 8): string
    {
        $code = $prefix . strtoupper(substr(uniqid(), -$length));
        return $code;
    }

    /**
     * Get fillable fields
     */
    public function getFillable(): array
    {
        return $this->fillable;
    }
}
