<?php

/**
 * Class CmdbMigrationService
 */
class CmdbMigrationService
{
    private $ci;
    private $cfsettings;
    private $cfdb;

    public function __construct()
    {
        $this->ci = &get_instance();
        $this->cfsettings = $this->ci->load->database('cfsettings', true);
        $this->cfdb = $this->ci->load->database('cfdb', true);
    }

    public function migrate(): void
    {
        $this->process(
            $this->cfdb,
            cmdbTable: '__cmdb',
            entriesTable: '__cmdb_host_entries',
            subEntriesTable: '__cmdb_host_subentries',
            identifier: 'hostkey',
        );
        $this->process(
            $this->cfsettings,
            cmdbTable: 'shared_host_groups_data',
            entriesTable: 'shared_host_groups_data_entries',
            subEntriesTable: 'shared_host_groups_data_subentries',
            identifier: 'group_id',
        );
    }

    /**
     * @return bool
     * @throws Exception
     */
    public function process(
        $pdo,
        string $cmdbTable,
        string $entriesTable,
        string $subEntriesTable,
        string $identifier,
    ): bool {
        $queryResponse = $pdo->query("SELECT 1 FROM $entriesTable LIMIT 1");
        if (!$queryResponse) {
            return false;
        }
        $isCmdbConfigurationEmpty = empty($queryResponse->result());
        $cmdbEntries = $pdo->query("SELECT * FROM $cmdbTable")->result();
        if (!$isCmdbConfigurationEmpty || sizeof($cmdbEntries) === 0) {
            return false;
        }

        $pdo->trans_start();

        try {
            foreach ($cmdbEntries as $entry) {
                $value = json_decode($entry->value);
                foreach ($value as $type => $typeData) {
                    $calculatedType = $type === 'variables' ? 'variable' : 'class';
                    foreach ($typeData as $itemName => $itemData) {
                        $tags = $itemData->tags ?? [];
                        $itemValue = $itemData->value ?? null;
                        $comment = $itemData->comment ?? '';

                        // Format tags for PostgreSQL array
                        $tagsArray = '{}';
                        if (!empty($tags)) {
                            $escapedTags = array_map(function ($tag): string {
                                return '"' . addslashes($tag) . '"';
                            }, (array) $tags);
                            $tagsArray = '{' . implode(',', $escapedTags) . '}';
                        }

                        $configData = [
                            $identifier => $entry->{$identifier},
                            'description' => $comment,
                            'name' => $itemName,
                            'tags' => $tagsArray,
                            'type' => $calculatedType,
                            'meta' => json_encode(['migrated_at' => date('Y-m-d H:i:s')]),
                        ];

                        $insertResult = $pdo->insert($entriesTable, $configData);
                        if (!$insertResult) {
                            throw new Exception('Failed to insert configuration: ' . $pdo->error()['message']);
                        }

                        $entry_id = $pdo->insert_id();
                        if (!$entry_id) {
                            throw new Exception('Failed to get configuration ID');
                        }

                        $configItemData = [
                            $identifier => $entry->{$identifier},
                            'entry_id' => $entry_id,
                            'item_name' => $itemName,
                            'item_value' => json_encode($itemValue),
                            'item_type' => $calculatedType,
                        ];

                        $insertItemResult = $pdo->insert($subEntriesTable, $configItemData);
                        if (!$insertItemResult) {
                            throw new Exception('Failed to insert configuration item: ' . $pdo->error()['message']);
                        }
                    }
                }
            }

            $pdo->trans_complete();
            if ($pdo->trans_status() === false) {
                throw new Exception('Transaction failed');
            }

            return true;
        } catch (Exception $e) {
            // Rollback transaction on error
            $pdo->trans_rollback();
            throw $e;
        }
    }
}
