<?php

namespace CMDB\v2\Models;

use CMDB\v2\Entities\CmdbHostEntry;

class CmdbItemModel
{
    private \PDO $dbConnection;
    private $cmdbTable = '__cmdb';
    private $entriesTable = '__cmdb_host_entries';
    private $subentriesTable = '__cmdb_host_subentries';
    private $view = 'cmdb_host_entries';
    private $username;

    private $columnNames = [
        'id',
        'hostkey',
        'created_at',
        'name',
        'description',
        'tags',
        'type',
        'meta',
        'entries',
    ];

    public function __construct(string $username)
    {
        $this->username = $username;
        $this->dbConnection = \CfdbPdo::getInstance()->getConnection();
    }

    public function create(CmdbHostEntry $cmdbHostEntry)
    {
        try {
            $this->dbConnection->beginTransaction();
            $sql = "INSERT INTO {$this->entriesTable} (hostkey, name, description, tags, type, meta) 
                VALUES (:hostkey, :name, :description, :tags, :type, :meta)";

            $this->dbConnection->prepare(query: $sql)->execute($cmdbHostEntry->toArray());

            $entryId = $this->dbConnection->lastInsertId();

            foreach ($cmdbHostEntry->subentries as $cmdbHostSubEntry) {
                $cmdbHostSubEntry->setEntryId($entryId);
                $subentriesSql = "INSERT INTO {$this->subentriesTable} (hostkey, entry_id, item_name, item_type, item_value) 
                VALUES (:hostkey, :entry_id, :item_name, :item_type, :item_value)";

                $this->dbConnection->prepare($subentriesSql)->execute($cmdbHostSubEntry->toArray());
            }

            $this->dbConnection->commit();
            return $entryId;
        } catch (\Exception $e) {
            $this->dbConnection->rollBack();
            syslog(LOG_ERR, "Failed to create CMDB host entry: " . $e->getMessage());
            throw $e;
        }
    }
    public function update(int $id, CmdbHostEntry $cmdbHostEntry)
    {
        try {
            $this->dbConnection->beginTransaction();
            $sql = "UPDATE {$this->entriesTable} SET name = :name, description = :description, tags = :tags, meta = :meta WHERE id = :id";
            $filteredValues = array_intersect_key($cmdbHostEntry->toArray(), array_flip(['name', 'description', 'tags', 'meta']));

            $this->dbConnection->prepare(query: $sql)->execute([...$filteredValues, 'id' => $id]);
            $this->dbConnection->prepare("DELETE FROM {$this->subentriesTable} WHERE entry_id = :entry_id")->execute(['entry_id' => $id]);
            foreach ($cmdbHostEntry->subentries as $cmdbHostSubEntry) {
                $cmdbHostSubEntry->setEntryId($id);
                $subentriesSql = "INSERT INTO {$this->subentriesTable} (hostkey, entry_id, item_name, item_type, item_value) 
                VALUES (:hostkey, :entry_id, :item_name, :item_type, :item_value)";
                $this->dbConnection->prepare($subentriesSql)->execute($cmdbHostSubEntry->toArray());
            }
            $this->dbConnection->commit();
        } catch (\Exception $e) {
            $this->dbConnection->rollBack();
            syslog(LOG_ERR, "Failed to update CMDB host entry: " . $e->getMessage());
            throw $e;
        }
    }

    public function getById(int $id): mixed
    {
        if ($id <= 0) {
            throw new \InvalidArgumentException('ID must be a positive integer');
        }

        $params = \Utils::processQueryApiParams([]);
        $quotedId = \DatabaseHelper::quote((string)$id);
        $params->setQuery("SELECT * FROM {$this->view} WHERE id = $quotedId");

        $result = json_decode(
            cfapi_query_post(
                $this->username,
                $params->query,
                $params->sortColumn,
                $params->sortDescending,
                $params->skip,
                $params->limit,
                $params->hostContextInclude,
                $params->hostContextExclude
            ),
            true
        );

        $row = $result['data'][0]['rows'][0] ?? null;
        if ($row !== null) {
            $this->processRow($row);
        }

        return $row;
    }

    public function getSubEntry(string $type, string $name, string $hostkey): mixed
    {
        $params = \Utils::processQueryApiParams([]);
        $quotedHostkey = \DatabaseHelper::quote($hostkey);
        $quotedType = \DatabaseHelper::quote($type);
        $quotedName = \DatabaseHelper::quote($name);
        $params->setQuery("SELECT id, entry_id, item_name, item_value FROM cmdb_host_subentries WHERE hostkey = $quotedHostkey AND item_type = $quotedType AND item_name = $quotedName");

        $result = json_decode(
            cfapi_query_post(
                $this->username,
                $params->query,
                $params->sortColumn,
                $params->sortDescending,
                $params->skip,
                $params->limit,
                $params->hostContextInclude,
                $params->hostContextExclude
            ),
            true
        );

        $row = $result['data'][0]['rows'][0] ?? null;

        if ($row) {
            $row = [
                'id' => (int) $row[0],
                'entry_id' => (int) $row[1],
                'item_name' => $row[2],
                'item_value' => $row[3]
            ];
        }

        return $row;
    }

    public function getPolicyConfigurationIds(string $hostkey, \QueryApiParamsDto $params): array
    {
        $quotedHostkey = \DatabaseHelper::quote($hostkey);
        $params->setQuery("SELECT meta->>'policyId' FROM {$this->view} WHERE hostkey = $quotedHostkey AND type = 'policy_configuration'");
        $result = json_decode(
            cfapi_query_post(
                $this->username,
                $params->query,
                $params->sortColumn,
                $params->sortDescending,
                $params->skip,
                $params->limit,
                $params->hostContextInclude,
                $params->hostContextExclude
            ),
            true
        );

        $rows = $result['data'][0]['rows'] ?? [];
        return array_column($rows, 0);
    }

    public function list(string $hostkey, \QueryApiParamsDto $params): array
    {
        $quotedHostkey = \DatabaseHelper::quote($hostkey);
        $params->setQuery("SELECT * FROM {$this->view} WHERE hostkey = $quotedHostkey");
        $result = json_decode(
            cfapi_query_post(
                $this->username,
                $params->query,
                $params->sortColumn,
                $params->sortDescending,
                $params->skip,
                $params->limit,
                $params->hostContextInclude,
                $params->hostContextExclude
            ),
            true
        );

        $rows = $result['data'][0]['rows'] ?? [];

        foreach ($rows as &$row) {
            $this->processRow($row);
        }
        return [
            'data' => $rows,
            'meta' => [
                'total' => $result['data'][0]['rowCount'],
                'page' => floor($params->skip / $params->limit) + 1,
                'count' => sizeof($rows),
            ]
        ];
    }

    public function delete(int $id)
    {
        return $this->dbConnection->prepare("DELETE FROM {$this->entriesTable} WHERE id = :id")->execute(['id' => $id]);
    }

    private function processRow(array &$row): void
    {
        $row[0] = (int) $row[0]; # id
        $row[7] = json_decode($row[7], false) ?? new \stdClass(); # meta
        $row[8] = json_decode($row[8], false) ?? []; # entries
        $row = array_combine($this->columnNames, $row);
    }
}
