<?php

namespace idoit\Module\SyneticsJdisc\Model;

use Idoit\Dto\Serialization\Serializer;
use idoit\Module\SyneticsJdisc\Controller\Table\SearchParams;
use idoit\Module\SyneticsJdisc\Model\Dto\Log;
use isys_jdisc_dao;
use Throwable;

class JDiscLogDao extends AbstractDao
{
    public const LOG_FIELDS = [
        'isys_jdisc_log__id' => 'id',
        'isys_jdisc_log__start_date' => 'start_date',
        'isys_jdisc_log__end_date' => 'end_date',
        'isys_jdisc_log__status' => 'status',
        'isys_jdisc_log__server_info' => 'server_info',
        'isys_jdisc_log__profile_info' => 'profile_info',
        'isys_jdisc_log__file_name' => 'file_name',
    ];

    public const SORTING_MAP = [
        'id'           => 'isys_jdisc_log__id',
        'start_date'   => 'isys_jdisc_log__start_date',
        'end_date'     => 'isys_jdisc_log__end_date',
        'server_info'  => 'isys_jdisc_log__server_info',
        'profile_info' => 'isys_jdisc_log__profile_info',
        'file_name'    => 'isys_jdisc_log__file_name',
        'status'       => 'isys_jdisc_log__status',
    ];

    /**
     * @param string|null       $condition
     * @param SearchParams|null $searchParams
     *
     * @return array
     * @throws \isys_exception_database
     */
    public function getData(?string $condition = null, ?SearchParams $searchParams = null): array
    {
        $result = [];
        $mapping = self::LOG_FIELDS;
        array_walk($mapping, function (&$item, $key) {
            $item = $key . ' AS ' . $this->convert_sql_text($item);
        });

        $query = 'SELECT ' . implode(',', $mapping) . ' FROM isys_jdisc_log WHERE TRUE ';
        if (null !== $condition) {
            $query .= ' AND ' . $condition;
        }

        if ($searchParams) {
            if ($searchParams->getSearchTerm()) {
                $searchTerm = $this->convert_sql_text('%' . $searchParams->getSearchTerm() . '%');
                $query .= " AND ("
                    . "isys_jdisc_log__start_date LIKE {$searchTerm} OR "
                    . "isys_jdisc_log__end_date LIKE {$searchTerm} OR "
                    . "isys_jdisc_log__server_info LIKE {$searchTerm} OR "
                    . "isys_jdisc_log__profile_info LIKE {$searchTerm} OR "
                    . "isys_jdisc_log__file_name LIKE {$searchTerm}"
                    . ")";
            }

            $sortId = $searchParams->getSort()?->getId() ?? null;
            if ($sortId && isset(self::SORTING_MAP[$sortId])) {
                $direction = $searchParams->getSort()?->isDesc() ? 'desc' : 'asc';
                $query .= ' ORDER BY ' . self::SORTING_MAP[$sortId] . ' ' . $direction;
            }
            $query .= " LIMIT {$searchParams->getPerPage()} OFFSET {$searchParams->getOffset()}";
        }

        $daoResult = $this->retrieve($query . ';');
        while ($row = $daoResult->get_row()) {
            $result[] = $this->processRow($row);
        }

        return $result;
    }

    /**
     * @param string|null $condition
     *
     * @return int
     * @throws \isys_exception_database
     */
    public function getCount(?string $condition = null): int
    {
        $daoResult = $this->retrieve('SELECT count(1) as count FROM isys_jdisc_log WHERE TRUE ' . ($condition ?: '') . ';');
        $result = $daoResult->get_row_value('count');
        if ($result === null) {
            return 0;
        }
        return intval($result);
    }

    /**
     * @param int $id
     * @return Log|null
     */
    public function get(int $id): ?Log
    {
        $log = current(
            $this->getJdiscDao()->get_entities(isys_jdisc_dao::C__LOGS, null, ['id' => $id])
        );

        if (!$log) {
            return null;
        }

        return $this->processRow($log);
    }

    /**
     * @param int $id
     * @return bool
     */
    public function delete(int $id): bool
    {
        try {
            $this->getJdiscDao()->delete(isys_jdisc_dao::C__LOGS, ['id' => $id]);
            return true;
        } catch (Throwable $e) {
            // @todo log if needed
            return false;
        }
    }

    /**
     * @param array $log
     * @return int|null
     */
    public function save(array $log): ?int
    {
        try {
            $data = [];
            foreach (self::LOG_FIELDS as $field) {
                $data[$field] = match ($field) {
                    'status' => $log[$field] === 'success' ? 1 : 0,
                    'server_info', 'profile_info' => json_encode($log[$field]),
                    default => $log[$field],
                };
            }
            if (empty($data['id'])) {
                unset($data['id']);
            }
            return $this->getJdiscDao()->save(isys_jdisc_dao::C__LOGS, $data);
        } catch (Throwable $e) {
            // @todo log if needed
            return null;
        }
    }

    /**
     * @param array $row
     * @return Log
     */
    private function processRow(array $row): Log
    {
        $data = [];
        foreach (self::LOG_FIELDS as $field) {
            $data[$field] = match ($field) {
                'status' => $row[$field] > 0 ? 'success' : 'error',
                'server_info', 'profile_info' => json_decode($row[$field], true),
                default => $row[$field],
            };
        }
        return Serializer::fromJson(Log::class, $data);
    }
}
