<?php

declare(strict_types=1);

class Energy_Pie extends IPSModule
{
    // Attribute keys for UI state
    private const ATTR_RANGE = 'Range';
    private const ATTR_DATE  = 'Date';

    public function Create(): void
    {
        parent::Create();

        // Source variables (logged counters, kWh)
        $this->RegisterPropertyInteger('VarProduction', 0);
        $this->RegisterPropertyInteger('VarConsumption', 0);
        $this->RegisterPropertyInteger('VarFeedIn', 0);
        $this->RegisterPropertyInteger('VarGrid', 0);

        // Default range for first startup
        $this->RegisterPropertyString('DefaultRange', 'day');

        // Persisted UI state
        $this->RegisterAttributeString(self::ATTR_RANGE, 'day');
        $this->RegisterAttributeString(self::ATTR_DATE, date('Y-m-d'));

        // Enable individual visualization (HTML-SDK)
        $this->SetVisualizationType(1);
    }

    public function ApplyChanges(): void
    {
        parent::ApplyChanges();

        // First-time init: if range is empty, set it to property default
        $range = $this->ReadAttributeString(self::ATTR_RANGE);
        if ($range === '') {
            $this->WriteAttributeString(self::ATTR_RANGE, $this->ReadPropertyString('DefaultRange'));
        }

        $date = $this->ReadAttributeString(self::ATTR_DATE);

        // Wenn Datum leer/ungültig oder in der Zukunft -> heute setzen
        if ($date === '' || !$this->isValidDate($date) || strtotime($date . ' 00:00:00') > time()) {
            $this->WriteAttributeString(self::ATTR_DATE, date('Y-m-d'));
        }

        // Push initial view data
        $this->RecalculateAndPush();
    }

    public function GetVisualizationTile(): string
    {
        $path = __DIR__ . '/module.html';
        if (!file_exists($path)) {
            return '<div style="padding:12px;font-family:sans-serif;">module.html fehlt</div>';
        }
        return file_get_contents($path);
    }

    public function RequestAction($Ident, $Value): void
    {
        switch ($Ident) {
            case 'SetRange':
                $range = (string)$Value;
                if (!in_array($range, ['day', 'week', 'month', 'total'], true)) {
                    throw new Exception('Invalid range: ' . $range);
                }
                $this->WriteAttributeString(self::ATTR_RANGE, $range);
                $this->RecalculateAndPush();
                break;

            case 'SetDate':
                // Expect YYYY-MM-DD from HTML <input type="date">
                $date = (string)$Value;
                if (!$this->isValidDate($date)) {
                    throw new Exception('Invalid date: ' . $date);
                }
                $this->WriteAttributeString(self::ATTR_DATE, $date);
                $this->RecalculateAndPush();
                break;

            case 'Prev':
            case 'Next':
            case 'Today':
                $this->ShiftDate($Ident);
                $this->RecalculateAndPush();
                break;

            case 'Refresh':
                $this->RecalculateAndPush();
                break;

            default:
                throw new Exception('Unknown Ident: ' . $Ident);
        }
    }

    /**
     * Compute values (later from archive), then push to tile via UpdateVisualizationValue.
     */
private function RecalculateAndPush(): void
{



    $range = $this->ReadAttributeString(self::ATTR_RANGE);
    $date  = $this->ReadAttributeString(self::ATTR_DATE);

    

    [$tStart, $tEnd] = $this->getRange($range, $date);

    // Debug-Container initialisieren (sonst "Undefined variable")
    $dbgProd = [];
    $dbgFeed = [];
    $dbgGrid = [];

    $prod = $this->readDelta($this->ReadPropertyInteger('VarProduction'), $tStart, $tEnd, $dbgProd);
    $feed = $this->readDelta($this->ReadPropertyInteger('VarFeedIn'),     $tStart, $tEnd, $dbgFeed);
    $grid = $this->readDelta($this->ReadPropertyInteger('VarGrid'),       $tStart, $tEnd, $dbgGrid);


    $hasData = (($dbgProd['count'] ?? 0) > 0) || (($dbgFeed['count'] ?? 0) > 0) || (($dbgGrid['count'] ?? 0) > 0);

    if (!$hasData && $range !== 'total') {
        // nimm Produktion als Referenz für "letzte Daten"
        $lastTs = $this->getLastLogTimestamp($this->ReadPropertyInteger('VarProduction'));

        if ($lastTs > 0) {
            $this->WriteAttributeString(self::ATTR_DATE, date('Y-m-d', $lastTs));

            // Range neu berechnen für dieses Datum
            $date = $this->ReadAttributeString(self::ATTR_DATE);
            [$tStart, $tEnd] = $this->getRange($range, $date);

            // Deltas nochmal holen (jetzt sollte count>0 werden)
            $prod = $this->readDelta($this->ReadPropertyInteger('VarProduction'), $tStart, $tEnd, $dbgProd);
            $feed = $this->readDelta($this->ReadPropertyInteger('VarFeedIn'),     $tStart, $tEnd, $dbgFeed);
            $grid = $this->readDelta($this->ReadPropertyInteger('VarGrid'),       $tStart, $tEnd, $dbgGrid);

            $hasData = (($dbgProd['count'] ?? 0) > 0) || (($dbgFeed['count'] ?? 0) > 0) || (($dbgGrid['count'] ?? 0) > 0);
        }
    }


    $house = $prod - $feed + $grid;
    if ($house < 0) $house = 0.0;

    $payload = [
        'range'  => $range,
        'date'   => $date,
        'tStart' => $tStart,
        'tEnd'   => $tEnd,
        'hasData' => $hasData,
        'values' => [
            'Produktion'    => (float)$prod,
            'Einspeisung'   => (float)$feed,
            'Netz'          => (float)$grid,
            'Hausverbrauch' => (float)$house
        ],
        'debug' => [
            'prod' => $dbgProd,
            'feed' => $dbgFeed,
            'grid' => $dbgGrid
        ]
    ];

    $this->UpdateVisualizationValue(json_encode($payload, JSON_THROW_ON_ERROR));
}


    /**
     * Range rules:
     * - day: chosen date 00:00:00 .. next day 00:00:00
     * - week: Monday 00:00:00 .. next Monday 00:00:00   (Mo–So)
     * - month: first of month 00:00:00 .. first of next month 00:00:00
     * - total: 0 .. now (placeholder)
     */
    private function getRange(string $range, string $dateYmd): array
    {
        $now = time();

        if ($range === 'total') {
            // Later: use oldest log timestamp as start
            return [0, $now];
        }

        $base = strtotime($dateYmd . ' 00:00:00');
        if ($base === false) {
            // fallback to today
            $base = strtotime(date('Y-m-d') . ' 00:00:00');
        }

        switch ($range) {
            case 'day':
                $start = $base;
                $end   = $start + 86400;
                return [$start, $end];

            case 'week':
                // date('N'): 1=Mon .. 7=Sun
                $dow = (int)date('N', $base);
                $start = $base - (($dow - 1) * 86400); // Monday
                $end   = $start + (7 * 86400);         // next Monday
                return [$start, $end];

            case 'month':
                $start = strtotime(date('Y-m-01 00:00:00', $base));
                $end   = strtotime(date('Y-m-01 00:00:00', strtotime('+1 month', $start)));
                return [$start, $end];

            default:
                return [$base, $base + 86400];
        }
    }


    private function readDelta(int $varId, int $tStart, int $tEnd, array &$dbg): float
    {
        $dbg = [
            'varId'     => $varId,
            'archiveId' => 0,
            'count'     => 0,
            'first'     => null,
            'last'      => null,
            'vStart'    => null,
            'vEnd'      => null
        ];

        if ($varId <= 0 || !IPS_VariableExists($varId)) {
            return 0.0;
        }

        $archiveID = IPS_GetInstanceListByModuleID('{43192F0B-135B-4CE7-A0A7-1475603F3060}')[0] ?? 0;
        $dbg['archiveId'] = $archiveID;

        if ($archiveID <= 0) {
            return 0.0;
        }

        $values = @AC_GetLoggedValues($archiveID, $varId, $tStart - 86400, $tEnd + 86400, 0);
        if (empty($values)) {
            return 0.0;
        }

        usort($values, static fn($a, $b) => (int)$a['TimeStamp'] <=> (int)$b['TimeStamp']);
        $dbg['count'] = count($values);

        $dbg['first'] = (float)$values[0]['Value'];
        $dbg['last']  = (float)$values[count($values) - 1]['Value'];

        $vStart = null;
        $vEnd   = null;

        foreach ($values as $v) {
            $ts = (int)$v['TimeStamp'];
            if ($ts <= $tStart) $vStart = (float)$v['Value'];
            if ($ts <= $tEnd)   $vEnd   = (float)$v['Value'];
            if ($ts > $tEnd) break;
        }

        if ($vStart === null) $vStart = $dbg['first'];
        if ($vEnd === null)   $vEnd   = $dbg['last'];

        $dbg['vStart'] = $vStart;
        $dbg['vEnd']   = $vEnd;

        $diff = $vEnd - $vStart;
        return ($diff < 0) ? 0.0 : (float)$diff;
    }

    private function getLastLogTimestamp(int $varId): int
    {
        if ($varId <= 0 || !IPS_VariableExists($varId)) {
            return 0;
        }

        $archiveID = IPS_GetInstanceListByModuleID('{43192F0B-135B-4CE7-A0A7-1475603F3060}')[0] ?? 0;
        if ($archiveID <= 0) {
            return 0;
        }

        // Hole den letzten Eintrag (0..now, limit=1 -> der neueste)
        $values = @AC_GetLoggedValues($archiveID, $varId, 0, time(), 1);
        if (empty($values)) {
            return 0;
        }

        // Bei limit=1 kommt genau ein Eintrag (der neueste)
        return (int)$values[0]['TimeStamp'];
    }



    /**
     * Buttons for quick navigation.
     */
    private function ShiftDate(string $action): void
    {
        $range = $this->ReadAttributeString(self::ATTR_RANGE);

        if ($range === 'total') {
            // total ignores date
            return;
        }

        if ($action === 'Today') {
            $this->WriteAttributeString(self::ATTR_DATE, date('Y-m-d'));
            return;
        }

        $date = $this->ReadAttributeString(self::ATTR_DATE);
        $base = strtotime($date . ' 00:00:00');
        if ($base === false) {
            $base = strtotime(date('Y-m-d') . ' 00:00:00');
        }

        $sign = ($action === 'Prev') ? -1 : 1;

        switch ($range) {
            case 'day':
                $base = strtotime(($sign === -1 ? '-1 day' : '+1 day'), $base);
                break;

            case 'week':
                $base = strtotime(($sign === -1 ? '-7 day' : '+7 day'), $base);
                break;

            case 'month':
                $base = strtotime(($sign === -1 ? '-1 month' : '+1 month'), $base);
                break;
        }

        $this->WriteAttributeString(self::ATTR_DATE, date('Y-m-d', $base));
    }

    private function isValidDate(string $ymd): bool
    {
        // expects YYYY-MM-DD
        if (!preg_match('/^\d{4}-\d{2}-\d{2}$/', $ymd)) {
            return false;
        }
        [$y, $m, $d] = array_map('intval', explode('-', $ymd));
        return checkdate($m, $d, $y);
    }


}

?>