<?php
declare(strict_types=1);
class Energy_Pie extends IPSModule
{
    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);
        // 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);
        // IMPORTANT: Timer calls global helper below (must exist!)
        $this->RegisterTimer('AutoPush', 0, 'IPS_RequestAction($_IPS["TARGET"], "Refresh", 1);');
    }
    public function ApplyChanges(): void
    {
        parent::ApplyChanges();
        // ensure range valid
        $range = $this->ReadAttributeString(self::ATTR_RANGE);
        if (!in_array($range, ['day', 'week', 'month', 'year', 'total'], true)) {
            $this->WriteAttributeString(self::ATTR_RANGE, 'day');
        }
        // ensure date valid (not empty/invalid/future)
        $date = $this->ReadAttributeString(self::ATTR_DATE);
        if ($date === '' || !$this->isValidDate($date) || strtotime($date . ' 00:00:00') > time()) {
            $this->WriteAttributeString(self::ATTR_DATE, date('Y-m-d'));
        }
        // Fullscreen-Fix: push periodically (adjust as you like)
        // 2000ms = alle 2 Sekunden (stabil, aber nicht ganz so brutal wie 1000ms)
        $this->SetTimerInterval('AutoPush', 2000);
        $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', 'year', 'total'], true)) {
                    return;
                }
                $this->WriteAttributeString(self::ATTR_RANGE, $range);
                $this->RecalculateAndPush();
                break;
            case 'SetDate':
                $date = (string)$Value;
                if (!$this->isValidDate($date)) {
                    return;
                }
                $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:
                // ignore unknown
                return;
        }
    }
    private function RecalculateAndPush(): void
    {
        $range = $this->ReadAttributeString(self::ATTR_RANGE);
        $date  = $this->ReadAttributeString(self::ATTR_DATE);
        [$tStart, $tEnd] = $this->getRange($range, $date);
        $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);
        $noDataHint = (!$hasData && $range !== 'total') ? 'Letzter Zeitpunkt' : '';


        // House = Prod - Feed + Grid
        $house = $prod - $feed + $grid;
        if ($house < 0) $house = 0.0;
        $payload = [
            'range'   => $range,
            'date'    => $date,
            'tStart'  => $tStart,
            'tEnd'    => $tEnd,
            'hasData' => $hasData,
            'noDataHint' => $noDataHint,
            'values'  => [
                'Produktion'    => (float)$prod,
                'Einspeisung'   => (float)$feed,
                'Netz'          => (float)$grid,
                'Hausverbrauch' => (float)$house
            ],
            // kannst du später entfernen
            'debug' => [
                'prod' => $dbgProd,
                'feed' => $dbgFeed,
                'grid' => $dbgGrid
            ]
        ];
        $this->UpdateVisualizationValue(json_encode($payload, JSON_THROW_ON_ERROR));
    }
    private function getRange(string $range, string $dateYmd): array
    {
        $now = time();
        if ($range === 'total') {
            return [0, $now];
        }
        $base = strtotime($dateYmd . ' 00:00:00') ?: strtotime(date('Y-m-d') . ' 00:00:00');
        switch ($range) {
            case 'day':
                return [$base, $base + 86400];
            case 'week':
                $dow = (int)date('N', $base); // 1=Mon..7=Sun
                $start = $base - (($dow - 1) * 86400);
                return [$start, $start + 7 * 86400];
            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];

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

            default:
                return [$base, $base + 86400];
        }
    }
    public function GetVisualizationPopup(): string
    {
    // Popup (Fullscreen) soll das gleiche HTML wie das Tile anzeigen
    $this->RecalculateAndPush();
    return $this->GetVisualizationTile();
    }
    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;
        }
        $firstTs = (int)$values[0]['TimeStamp'];
        $lastTs  = (int)$values[count($values) - 1]['TimeStamp'];
        if ($firstTs > $lastTs) {
            $values = array_reverse($values);
        }
        $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;
        }
        $values = @AC_GetLoggedValues($archiveID, $varId, 0, time(), 1);
        if (empty($values)) {
            return 0;
        }
        return (int)$values[0]['TimeStamp'];
    }
    private function ShiftDate(string $action): void
    {
        $range = $this->ReadAttributeString(self::ATTR_RANGE);
        if ($range === 'total') {
            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') ?: 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;
            case 'year':
                $base = strtotime(($sign === -1 ? '-1 year' : '+1 year'), $base);
                break;
        }
        $this->WriteAttributeString(self::ATTR_DATE, date('Y-m-d', $base));
    }
    private function isValidDate(string $ymd): bool
    {
        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);
    }
}

?>
