<?php

namespace Lucent\Revision;

use Illuminate\Support\Facades\DB;
use Lucent\Edge\Edge;
use Lucent\Edge\EdgeCollection;
use Lucent\Primitive\Collection;
use Lucent\Record\File;
use Lucent\Record\RecordData;
use Lucent\Record\System;
use PhpOption\Option;
use stdClass;

class RevisionRepo
{

    private string $table = "revisions";

    public function create(Revision $revision): string
    {
        $revisionDB = $this->toDB($revision);
        DB::table($this->table)->insert($revisionDB);
        return $revision->id;
    }


    /**
     * @return Collection<Revision>
     **/
    public function getByRecordId(string $rid): Collection
    {
        $revisions = DB::table($this->table)
            ->where("recordId", $rid)
            ->get()
            ->map([$this, 'fromDB'])
            ->sortByDesc("_sys.version")
            ->toArray();

        return new Collection($revisions);
    }

    public function cleanupRecord(string $rid, int $numKeep): void
    {
        $revisionIds = DB::table($this->table)
            ->where("recordId", $rid)
            ->orderBy("_sys->version", "desc")
            ->limit(100)
            ->skip($numKeep)
            ->get()
            ->pluck("id");

        DB::table($this->table)
            ->whereIn("id", $revisionIds)
            ->delete();
    }


    /**
     * @return Option<Revision>
     */
    public function getByRecordIdAndVersion(string $rid, int $version): Option
    {

        $res = DB::table($this->table)
            ->where("recordId", $rid)
            ->where('_sys->version', $version)->first();

        if (empty($res)) {
            return none();
        }

        return some($this->fromDB($res));
    }

    public function toDB(Revision $revision): array
    {
        return [
            "id" => $revision->id,
            "recordId" => $revision->recordId,
            "schema" => $revision->schema,
            "_sys" => json_encode($revision->_sys),
            "_file" => json_encode($revision->_file),
            "data" => json_encode($revision->data),
            "_edges" => $revision->_edges->toJson(),
        ];
    }

    public function fromDB(stdClass $data): Revision
    {
        $file = json_decode($data->_file, true);
        if (!empty($file)) {

            $file = new File(...$file);
        } else {
            $file = null;
        }

        $edges = array_map(fn($e) => Edge::fromArray($e), json_decode($data->_edges ?? "[]", true));

        return new Revision(
            id: $data->id,
            recordId: $data->recordId,
            schema: $data->schema,
            _sys: System::fromArray(json_decode($data->_sys, true)),
            data: new RecordData(json_decode($data->data, true)),
            _edges: new EdgeCollection(...$edges),
            _file: $file
        );
    }
}
