<?php

namespace Lucent\Http\Controller;

use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Lucent\Account\AccountService;
use Lucent\Account\AuthService;
use Lucent\Channel\ChannelService;
use Lucent\LucentException;
use Lucent\Query\Operator;
use Lucent\Query\Query;
use Lucent\Record\Manager;
use Lucent\Record\QueryRecord;
use Lucent\Record\RecordService;
use Lucent\Schema\FieldInterface;
use Lucent\Schema\System;
use Lucent\Schema\Validator\ValidatorException;
use Lucent\Svelte\Svelte;
use function Lucent\Response\fail;
use function Lucent\Response\ok;

class RecordController extends Controller
{
    public function __construct(
        private readonly RecordService  $recordService,
        private readonly AccountService $accountService,
        private readonly AuthService    $authService,
        private readonly ChannelService $channelService,
        private readonly Svelte         $svelte,
        private readonly Query          $query,
        private readonly Manager        $recordManager
    )
    {
    }

    public function index(Request $request)
    {
        $schemaName = $request->route("schemaName");

        if (!in_array($schemaName, $this->accountService->currentReadableSchemas())) {
            return $this->svelte->render(
                layout: "channel",
                view: "recordNotFound",
                title: "Schema Not Found",
            );
        }

        $users = $this->accountService->all();
        $schema = $this->channelService->getSchema($schemaName)->get();
        $urlParams = $request->all();
        $sort = data_get($urlParams, "sort") ?? $schema->sortBy;
        $filter = data_get($urlParams, "filter") ?? [];

        $arguments = array_merge([
            "schema" => $schema->name,
            "status_in" => "draft,published",
        ], $filter);


        $skip = data_get($urlParams, "skip") ?? 0;
        $limit = 30;
        $records = [];
        $graphArray = null;
        $graph = $this->query
            ->filter($arguments)
            ->limit($limit)
            ->status(explode(",", $arguments["status_in"]))
            ->skip($skip)
            ->sort($sort)
            ->childrenFields($schema?->visible ?? [])
            ->childrenDepth(1)
            ->parentsDepth(0)
            ->runWithCount();


        $records = $graph->getRootRecords()->toArray();


        $data = [
            "schemas" => $this->channelService->channel->schemas,
            "schema" => $schema,
            "users" => $users,
            "records" => $records,
            "graph" => toArray($graph),
            "systemFields" => array_values(System::list()),
            "operators" => array_values(Operator::list()),
            "sortParam" => $sort,
            "sortField" => $schema->fields->merge(array_values(System::list()))->firstWhere(fn($field) => $field->name === $sort || "-" . $field->name === $sort || "data." . $field->name === $sort || "-data." . $field->name === $sort),
            "limit" => $limit,
            "skip" => $skip,
            "total" => $graph->total ?? 0,
            "filter" => $request->input("filter") ?? [],
            "inModal" => true,
            "isWritable" => in_array($schemaName, $this->accountService->currentWritableSchemas())
        ];

        if ($request->ajax()) {
            $data["modalUrl"] = $request->fullUrl();
            if (str_starts_with(config("lucent.url"), "https")) {
                $data["modalUrl"] = str_replace("http://", "https://", $request->fullUrl());
            }
            return $data;
        }
        $data["inModal"] = false;
        return $this->svelte->render(
            layout: "channel",
            view: "contentIndex",
            title: "Records",
            data: $data
        );
    }

    public function exportCSV(Request $request)
    {
        $schemaName = $request->route("schemaName");
        $schema = $this->channelService->channel->schemas->where("name", $schemaName)->first();

        $urlParams = $request->all();

        $sort = data_get($urlParams, "sort") ?? "-_sys.updatedAt";
        $filter = data_get($urlParams, "filter") ?? [];
        $arguments = array_merge([
            "schema" => $schema->name,
            "status_in" => "draft,published",
        ], $filter);


        $records = $this->query
            ->filter($arguments)
            ->limit(-1)
            ->status(explode(",", $arguments["status_in"]))
            // ->skip($skip)
            ->sort($sort)
            ->run()
            ->records;

        header('Content-Type: application/csv');
        header('Content-Disposition: attachment; filename="' . $schemaName . '.csv";');
        $handle = fopen('php://output', 'w');
        $csvRow = ["id", ...array_keys($records[0]->data->toArray())];
        fputcsv($handle, $csvRow, ',');
        foreach ($records as $record) {
            $csvRow = [$record->id, ...$record->data->toArray()];
            $csvRow = array_values($csvRow);
            fputcsv($handle, $csvRow, ',');
        }
        fclose($handle);
        echo $handle;
        exit;
    }

    public function new(Request $request)
    {
        if (!in_array($request->input("schema"), $this->accountService->currentWritableSchemas())) {
            return $this->svelte->render(
                layout: "channel",
                view: "recordNotFound",
                title: "Schema Not Found",
            );
        }

        $schema = $this->channelService->channel->schemas->where("name", $request->input("schema"))->first();
        $recordHistory = $this->recordManager->fromSession($request->session())->getRecords();
        $record = $this->recordService->createEmpty($schema, $this->authService->currentUserId());
        $queryRecord = QueryRecord::fromRecord($record);
        return $this->svelte->render(
            layout: "channel",
            view: "recordEdit",
            title: "New Record",
            data: [
                "schema" => $schema,
                "record" => $queryRecord,
                "recordHistory" => $recordHistory,
                "isCreateMode" => true,
                "isWritable" => in_array($record->schema, $this->accountService->currentWritableSchemas())
            ]
        );
    }


    public function newInline(Request $request)
    {
        if (!in_array($request->input("schema"), $this->accountService->currentWritableSchemas())) {
            return $this->svelte->render(
                layout: "channel",
                view: "recordNotFound",
                title: "Schema Not Found",
            );
        }

        $schema = $this->channelService->getSchema($request->input("schema"))->get();
        $record = $this->recordService->createEmpty($schema);
        $queryRecord = QueryRecord::fromRecord($record);

        return [
            "schema" => $schema,
            "record" => $queryRecord,
            "isCreateMode" => true,
        ];
    }

    public function edit(Request $request)
    {
        $rid = $request->route("rid");


        $graph = $this->query
            ->filter(["id" => $rid])
            ->limit(1)
            ->skip(0)
            ->childrenDepth(2)
            ->childrenLimit(200)
            ->parentsDepth(1)
            ->parentsLimit(200)
            ->run();

        if ($graph->records->isEmpty()) {
            return $this->svelte->render(
                layout: "channel",
                view: "recordNotFound",
                title: "Record Not Found",
            );
        }

        $record = $graph->records->first();

        if (!in_array($record->schema, $this->accountService->currentReadableSchemas())) {
            return $this->svelte->render(
                layout: "channel",
                view: "recordNotFound",
                title: "Schema Not Found",
            );
        }

        $schema = $this->channelService->getSchema($record->schema)->get();
        $recordHistory = $this->recordManager->fromSession($request->session())->push($rid)->getRecords($rid);
        return $this->svelte->render(
            layout: "channel",
            view: "recordEdit",
            title: "Edit Record",
            data: [
                "schema" => $schema,
                "graph" => toArray($graph),
                "record" => toArray($record),
                "users" => $this->accountService->all(),
                "recordHistory" => $recordHistory,
                "isWritable" => in_array($record->schema, $this->accountService->currentWritableSchemas())
            ]
        );
    }


//    public function editInline(Request $request)
//    {
//        $rid = $request->route("rid");
//
//        $graph = $this->query
//            ->filter(["id" => $rid])
//            ->limit(1)
//            ->childrenDepth(2)
//            ->parentsDepth(1)
//            ->run();
//
//        $record = $graph->records->first();
//
//        if(!in_array($record->schema,$this->accountService->currentReadableSchemas())){
//            return $this->svelte->render(
//                layout: "channel",
//                view: "recordNotFound",
//                title: "Schema Not Found",
//            );
//        }
//
//        return ok(
//            [
//                "graph" => toArray($graph),
//                "record" => toArray($record)
//            ]
//        );
//    }


    public function suggestions(Request $request)
    {
        $arguments = [
            "schema" => $request->input("schema"),
        ];

        if ($request->input("value")) {
            if (in_array($request->input("ui"), ["text", "date"])) {
                $arguments["data." . $request->input("field") . "_regex"] = $request->input("value");
            } elseif ($request->input("ui") == "number") {
                $arguments["data." . $request->input("field") . "_eqnum"] = floatval($request->input("value"));
            } elseif ($request->input("ui") == "date") {
            } elseif ($request->input("ui") == "search") {
                $arguments["search_regex"] = $request->input("value");
            }
        }

        $records = $this->query
            ->filter($arguments)
            ->limit(10)
            ->tree();

        if ($records->isEmpty()) {
            return ok([]);
        }

        return ok($records->toArray());
    }

    public function save(Request $request)
    {
        $recordId = $request->input("record.id");
        try {

            if ($request->input("isCreateMode")) {
                $recordId = $this->recordService->create(
                    schemaName: $request->input("record.schema"),
                    data: $request->input("record.data"),
                    id: $recordId ?? "",
                    file: $request->input("record._file") ?? [],
                    edges: $request->input("edges") ?? [],
                    status: $request->input("record.status"),
                    uploadFromUrl: ""
                );
            } else {

                $this->recordService->update(
                    id: $request->input("record.id"),
                    data: $request->input("record.data"),
                    status: $request->input("record.status"),
                    edges: $request->input("edges"),
                    updateEdges: true,
                );
            }

            $newGraph = $this->query
                ->filter(["id" => $recordId])
                ->limit(10)
                ->childrenDepth(1)
                ->parentsDepth(1)
                ->run();

        } catch (ValidatorException $th) {
            return fail($th->getValidatorErrors());
        } catch (LucentException $th) {
            return fail($th);
        }
        return ok(toArray($newGraph));
    }


    public function clone(Request $request)
    {
        try {

            $newRecordId = $this->recordService->clone(
                recordId: $request->route("rid"),
            );
        } catch (LucentException $th) {
            return fail($th);
        } catch (ValidatorException $e) {
            return fail($e);
        }
        return ok(["id" => $newRecordId]);
    }

    public function status(Request $request)
    {

        $ids = array_map(fn($rec) => $rec["id"], $request->input("records"));


        $this->recordService->changeStatusBulk(
            status: $request->route("status"),
            recordsIds: $ids,
        );
        return ok();
    }

    public function emptyTrash(Request $request)
    {

        $this->recordService->emptyTrash($request->route("schemaName"));
        return redirect($this->channelService->channel->lucentUrl . "/content/" . $request->route("schemaName"));
    }

    public function delete(Request $request)
    {
        $ids = $request->input("ids");

        try {
            $this->recordService->deleteMany($ids);
        } catch (Throwable $th) {
            return fail($th);
        }
        return ok();
    }

    public function rollback(Request $request)
    {
        try {
            $this->recordService->rollback(
                recordId: $request->route("rid"),
                version: (int)$request->route("version")
            );
        } catch (ValidatorException $th) {
            return fail($th->getFirstValidatorError());
        } catch (LucentException|Throwable $th) {
            return fail($th);
        }
        return ok();
    }
}
