<?php

namespace classes;
require_once WD . '/modules/attachments/attachments_c.php';

use attachments\helpers\attachment_content;
use Exception;
use interfaces\attachments_i;
use attachments\helpers\attachment;
use attachments\attachments_c;
use objects\object_attachments_o;

class attachments implements attachments_i
{
    /**
     * The configuration of the module
     * @var attachments_c $config
     */
    public attachments_c $config;
    public function __construct()
    {
        $this->config = new attachments_c();
    }

    /**
     * @inheritDoc
     */
    public function list(string $type, int $object_id, array $options = []): array
    {
        $rows = $this->fetchAttachmentRows($type, [$object_id], $options);
        return array_map(fn(array $row): attachment => $this->toAttachment($row), $rows);
    }

    /**
     * List attachments for multiple objects in one query.
     *
     * @param string $type
     * @param int[] $object_ids
     * @param array $options
     * @return array<int, attachment[]>
     */
    public function listMany(string $type, array $object_ids, array $options = []): array
    {
        $object_ids = array_values(array_unique(array_filter(array_map('intval', $object_ids), static fn(int $id): bool => $id > 0)));
        if (empty($object_ids)) {
            return [];
        }

        $rows = $this->fetchAttachmentRows($type, $object_ids, $options);
        $grouped = [];
        foreach ($object_ids as $object_id) {
            $grouped[$object_id] = [];
        }

        foreach ($rows as $row) {
            $object_id = (int)($row['object_id'] ?? 0);
            if (!isset($grouped[$object_id])) {
                $grouped[$object_id] = [];
            }
            $grouped[$object_id][] = $this->toAttachment($row);
        }

        return $grouped;
    }

    /**
     * @inheritDoc
     * @throws Exception If the attachment is not found
     */
    public function get(int $attachment_id): ?object_attachments_o
    {
        return (new object_attachments_o())->select($attachment_id) ?: null;
    }
    /**
     * Format an object_attachments_o to an attachment object
     * @param object_attachments_o $object_attachments_o The object to format
     * @return attachment The formatted attachment
     * @throws Exception If the attachment cannot be formatted
     */
    public function format(object_attachments_o $object_attachments_o): attachment
    {
        $data = (object)$object_attachments_o->toArray();
        // If content is JSON, decode it
        if (is_string($data->content) && $data->content !== '' && $data->content[0] === '{') {
            $data->content = json_decode($data->content, true);
        }
        return (new attachment())->populate($data);
    }

    /**
     * @inheritDoc
     * @throws Exception If the attachment cannot be created
     */
    public function create(string $type, int $object_id, attachment_content $attachment_content): int|bool
    {
        $object_attachments_o = new object_attachments_o();
        $object_attachments_o->add([
            'object_type' => $type,
            'object_id' => $object_id,
            'content' => json_encode($attachment_content->toArray()), // Store as JSON
        ]);
        return $object_attachments_o->id ?: false;
    }

    /**
     * @inheritDoc
     * @throws Exception If the attachment cannot be deleted
     */
    public function delete(int $attachment_id): bool
    {
        $object_attachments_o = new object_attachments_o();
        if ($object_attachments_o->select($attachment_id)) {
            $object_attachments_o->delete();
            return true;
        }
        return false;
    }

    /**
     * @inheritDoc
     * @throws Exception If the attachment cannot be updated
     */
    public function update(int $attachment_id, attachment_content $attachment_content): bool
    {
        $object_attachments_o = new object_attachments_o();
        if ($object_attachments_o->select($attachment_id)) {
            $object_attachments_o->content->set(json_encode($attachment_content->toArray()));
            return true;
        }
        return false;
    }

    protected function fetchAttachmentRows(string $type, array $object_ids, array $options = []): array
    {
        $options = $this->normalizeAttachmentOptions($options);
        $rawType = trim($type, '`');
        $objectTypes = array_values(array_unique([
            $rawType,
            '`' . $rawType . '`',
        ]));

        return (new object_attachments_o())->getFieldsWhereIn([
            'object_type' => $objectTypes,
            'object_id' => $object_ids,
            'deleted_at' => null
        ], $options);
    }

    protected function toAttachment(array $item): attachment
    {
        $payload = (object)$item;
        $payload->content = json_decode((string)$payload->content, true);
        return (new attachment())->populate($payload);
    }

    protected function normalizeAttachmentOptions(array $options): array
    {
        if (count($options) === 0) {
            return ['id', 'content', 'created_at', 'object_id', 'object_type', 'created_at', 'updated_at'];
        }
        return $options;
    }
}
