<?php

namespace SoftminalB2B\Support;

use SoftminalB2B\Models\Model;

if (!defined('ABSPATH')) {
    exit;
}

class Collection implements \ArrayAccess, \Countable, \IteratorAggregate
{
    private array $items = [];

    public function __construct(array $items = [])
    {
        $this->items = $items;
    }

    public function toArray()
    {
        return array_map(function ($value) {
            return $value instanceof Model ? $value->toArray() : $value;
        }, $this->items);
    }

    public function toJson(): string
    {
        return wp_json_encode($this->toArray());
    }

    public function map(callable $callback): self
    {
        return new self(array_map($callback, $this->items));
    }

    public function filter(?callable $callback = null): self
    {
        if ($callback === null) {
            return new self(array_values(array_filter($this->items)));
        }

        return new self(array_values(array_filter($this->items, $callback)));
    }

    public function pluck($value, $key = null): self
    {
        $results = [];

        foreach ($this->items as $item) {
            $itemValue = $item instanceof Model ? $item->$value : ($item[$value] ?? null);

            if ($key !== null) {
                $itemKey = $item instanceof Model ? $item->$key : ($item[$key] ?? null);
                $results[$itemKey] = $itemValue;
            } else {
                $results[] = $itemValue;
            }
        }

        return new self($results);
    }

    public function first()
    {
        return $this->items[0] ?? null;
    }

    public function last()
    {
        if (empty($this->items)) {
            return null;
        }

        return $this->items[array_key_last($this->items)];
    }

    public function isEmpty(): bool
    {
        return empty($this->items);
    }

    public function isNotEmpty(): bool
    {
        return !$this->isEmpty();
    }

    public function keyBy($key): self
    {
        $results = [];

        foreach ($this->items as $item) {
            $keyValue = $item instanceof Model ? $item->$key : ($item[$key] ?? null);
            if ($keyValue !== null) {
                $results[$keyValue] = $item;
            }
        }

        return new self($results);
    }

    public function values(): self
    {
        return new self(array_values($this->items));
    }

    public function getIterator(): \Traversable
    {
        return new \ArrayIterator($this->items);
    }

    public function offsetExists($offset): bool
    {
        return isset($this->items[$offset]);
    }

    public function offsetGet($offset): mixed
    {
        return $this->items[$offset] ?? null;
    }

    public function offsetSet($offset, $value): void
    {
        if (is_null($offset)) {
            $this->items[] = $value;
        } else {
            $this->items[$offset] = $value;
        }
    }

    public function offsetUnset($offset): void
    {
        unset($this->items[$offset]);
    }

    public function count(): int
    {
        return count($this->items);
    }
}
