<?php

declare(strict_types=1);

namespace Lab;

final class Cart
{
    public static function boot(string $channel = 'kiosk'): void
    {
        if (! isset($_SESSION['lab_sid'])) {
            $_SESSION['lab_sid'] = bin2hex(random_bytes(16));
        }
        if (! isset($_SESSION['lab_cart']) || ! is_array($_SESSION['lab_cart'])) {
            $_SESSION['lab_cart'] = self::empty($channel);
        }
        if (! isset($_SESSION['lab_cart']['channel'])) {
            $_SESSION['lab_cart']['channel'] = $channel;
        }
    }

    public static function reset(string $channel = 'kiosk'): void
    {
        $oldSid = (string) ($_SESSION['lab_sid'] ?? '');
        $_SESSION['lab_sid'] = bin2hex(random_bytes(16));
        $_SESSION['lab_cart'] = self::empty($channel);
        if ($oldSid !== '') {
            self::purgeUploads($oldSid);
        }
    }

    public static function sid(): string
    {
        self::boot();

        return (string) $_SESSION['lab_sid'];
    }

    public static function get(): array
    {
        self::boot();

        return $_SESSION['lab_cart'];
    }

    public static function setChannel(string $channel): void
    {
        self::boot($channel);
        $_SESSION['lab_cart']['channel'] = $channel === 'web' ? 'web' : 'kiosk';
    }

    public static function addPhoto(array $photo): void
    {
        self::boot();
        $_SESSION['lab_cart']['photos'][$photo['id']] = $photo;
        if (! isset($_SESSION['lab_cart']['edits'][$photo['id']])) {
            $_SESSION['lab_cart']['edits'][$photo['id']] = self::defaultEdit();
        }
        if (! isset($_SESSION['lab_cart']['options'])) {
            $_SESSION['lab_cart']['options'] = self::defaultOptions();
        }
    }

    public static function removePhoto(string $id): void
    {
        self::boot();
        unset($_SESSION['lab_cart']['photos'][$id], $_SESSION['lab_cart']['edits'][$id]);
    }

    public static function applyEdits(array $edits): void
    {
        self::boot();
        foreach ($edits as $id => $edit) {
            if (! isset($_SESSION['lab_cart']['photos'][$id])) {
                continue;
            }
            $current = $_SESSION['lab_cart']['edits'][$id] ?? self::defaultEdit();
            if (isset($edit['fit']) && in_array($edit['fit'], ['fill', 'fit'], true)) {
                $current['fit'] = $edit['fit'];
            }
            if (isset($edit['rotate'])) {
                $current['rotate'] = ((int) $edit['rotate'] % 360 + 360) % 360;
            }
            if (isset($edit['straighten'])) {
                $current['straighten'] = max(-15, min(15, (float) $edit['straighten']));
            }
            if (isset($edit['caption'])) {
                $cap = trim((string) $edit['caption']);
                $current['caption'] = mb_substr($cap, 0, 80);
            }
            if (isset($edit['brightness'])) {
                $current['brightness'] = max(-80, min(80, (int) $edit['brightness']));
            }
            if (isset($edit['contrast'])) {
                $current['contrast'] = max(-80, min(80, (int) $edit['contrast']));
            }
            if (isset($edit['saturation'])) {
                $current['saturation'] = max(-80, min(80, (int) $edit['saturation']));
            }
            if (isset($edit['midtones'])) {
                $current['midtones'] = max(-80, min(80, (int) $edit['midtones']));
            }
            foreach (['wbR', 'wbG', 'wbB'] as $wbKey) {
                if (isset($edit[$wbKey])) {
                    $current[$wbKey] = max(0.55, min(1.55, (float) $edit[$wbKey]));
                }
            }
            if (isset($edit['filter']) && in_array((string) $edit['filter'], ['none', 'bw', 'sepia', 'vivid'], true)) {
                $current['filter'] = (string) $edit['filter'];
            }
            if (isset($edit['flip'])) {
                $current['flip'] = ((int) $edit['flip']) ? 1 : 0;
            }
            if (isset($edit['sharpen'])) {
                $current['sharpen'] = ((int) $edit['sharpen']) ? 1 : 0;
            }
            $paperOri = $edit['paper_ori'] ?? $edit['paperOri'] ?? null;
            if (is_string($paperOri) && in_array($paperOri, ['auto', 'portrait', 'landscape'], true)) {
                $current['paper_ori'] = $paperOri;
            }
            if (isset($edit['crop']) && is_array($edit['crop'])) {
                $current['crop'] = [
                    'x' => self::clamp((float) ($edit['crop']['x'] ?? 0)),
                    'y' => self::clamp((float) ($edit['crop']['y'] ?? 0)),
                    'w' => max(0.05, min(1, (float) ($edit['crop']['w'] ?? 1))),
                    'h' => max(0.05, min(1, (float) ($edit['crop']['h'] ?? 1))),
                ];
            }
            if (isset($edit['qty']) && is_array($edit['qty'])) {
                $qty = [];
                foreach ($edit['qty'] as $sku => $n) {
                    $n = (int) $n;
                    if ($n > 0 && $n <= 99) {
                        $qty[(string) $sku] = $n;
                    }
                }
                $current['qty'] = $qty;
            }
            $_SESSION['lab_cart']['edits'][$id] = $current;
        }
    }

    public static function snapshot(array $products): array
    {
        $cart = self::get();
        $photos = [];
        $printCents = 0;
        $printCount = 0;
        foreach ($cart['photos'] as $id => $photo) {
            $edit = $cart['edits'][$id] ?? self::defaultEdit();
            $qtyMap = is_array($edit['qty'] ?? null) ? $edit['qty'] : [];
            $lines = [];
            foreach ($qtyMap as $sku => $qty) {
                $product = $products[$sku] ?? null;
                if (! $product || $qty < 1) {
                    continue;
                }
                $lineCents = (int) $product['price_cents'] * $qty;
                $printCents += $lineCents;
                $printCount += $qty;
                $lines[] = [
                    'sku' => $sku,
                    'name' => $product['name'],
                    'qty' => $qty,
                    'unit_cents' => (int) $product['price_cents'],
                    'line_cents' => $lineCents,
                ];
            }
            $photos[] = [
                'id' => $id,
                'name' => $photo['original_name'],
                'width' => $photo['width'],
                'height' => $photo['height'],
            'thumb' => Helpers::url('/api/thumb/' . $id),
            'preview' => Helpers::url('/api/preview/' . $id),
            'original' => Helpers::url('/api/original/' . $id),
            'fit' => $edit['fit'],
                'rotate' => $edit['rotate'],
                'straighten' => (float) ($edit['straighten'] ?? 0),
                'caption' => (string) ($edit['caption'] ?? ''),
                'brightness' => (int) ($edit['brightness'] ?? 0),
                'contrast' => (int) ($edit['contrast'] ?? 0),
                'saturation' => (int) ($edit['saturation'] ?? 0),
                'midtones' => (int) ($edit['midtones'] ?? 0),
                'wbR' => (float) ($edit['wbR'] ?? 1),
                'wbG' => (float) ($edit['wbG'] ?? 1),
                'wbB' => (float) ($edit['wbB'] ?? 1),
                'filter' => (string) ($edit['filter'] ?? 'none'),
                'flip' => (int) ($edit['flip'] ?? 0),
                'sharpen' => (int) ($edit['sharpen'] ?? 0),
                'paper_ori' => (string) ($edit['paper_ori'] ?? 'auto'),
                'crop' => $edit['crop'],
                'qty' => empty($qtyMap) ? new \stdClass() : $qtyMap,
                'lines' => $lines,
            ];
        }

        return [
            'sid' => self::sid(),
            'channel' => $cart['channel'] ?? 'kiosk',
            'photos' => $photos,
            'print_count' => $printCount,
            'print_cents' => $printCents,
            'print_count' => $printCount,
            'print_cents' => $printCents,
            'options' => $cart['options'] ?? self::defaultOptions(),
            'rush_cents' => (int) ($GLOBALS['lab_config']['shipping']['rush_cents'] ?? 200),
            'acs_cents' => (int) ($GLOBALS['lab_config']['shipping']['acs_cents'] ?? 490),
        ];
    }

    public static function photo(string $id): ?array
    {
        $cart = self::get();

        return $cart['photos'][$id] ?? null;
    }

    public static function edit(string $id): array
    {
        $cart = self::get();

        return $cart['edits'][$id] ?? self::defaultEdit();
    }

    public static function applyOptions(array $options): void
    {
        self::boot();
        $current = $_SESSION['lab_cart']['options'] ?? self::defaultOptions();
        if (isset($options['paper']) && in_array($options['paper'], ['glossy', 'matte'], true)) {
            $current['paper'] = $options['paper'];
        }
        if (isset($options['border']) && in_array($options['border'], ['none', 'white'], true)) {
            $current['border'] = $options['border'];
        }
        if (array_key_exists('rush', $options)) {
            $current['rush'] = ! empty($options['rush']) ? 1 : 0;
        }
        if (isset($options['payment']) && in_array($options['payment'], ['counter', 'cash', 'card'], true)) {
            $current['payment'] = $options['payment'];
        }
        if (isset($options['product']) && in_array($options['product'], ['prints', 'calendar', 'docs'], true)) {
            $current['product'] = $options['product'];
        }
        if (isset($options['calendar_style']) && in_array($options['calendar_style'], ['wall', 'months', 'classic'], true)) {
            $current['calendar_style'] = $options['calendar_style'];
        }
        if (isset($options['lang']) && in_array($options['lang'], ['el', 'en'], true)) {
            $current['lang'] = $options['lang'];
        }
        $_SESSION['lab_cart']['options'] = $current;
    }

    public static function duplicatePhoto(string $id, callable $copier): ?string
    {
        self::boot();
        $photo = $_SESSION['lab_cart']['photos'][$id] ?? null;
        if (! $photo) {
            return null;
        }
        $copy = $copier($photo);
        $copyId = (string) $copy['id'];
        $_SESSION['lab_cart']['photos'][$copyId] = $copy;
        $edit = $_SESSION['lab_cart']['edits'][$id] ?? self::defaultEdit();
        $_SESSION['lab_cart']['edits'][$copyId] = json_decode(json_encode($edit), true) ?: self::defaultEdit();

        return $copyId;
    }

    private static function defaultEdit(): array
    {
        return [
            'fit' => 'fill',
            'rotate' => 0,
            'straighten' => 0,
            'caption' => '',
            'brightness' => 0,
            'contrast' => 0,
            'saturation' => 0,
            'midtones' => 0,
            'wbR' => 1,
            'wbG' => 1,
            'wbB' => 1,
            'filter' => 'none',
            'flip' => 0,
            'sharpen' => 0,
            'paper_ori' => 'auto',
            'crop' => ['x' => 0, 'y' => 0, 'w' => 1, 'h' => 1],
            'qty' => [],
        ];
    }

    private static function defaultOptions(): array
    {
        return [
            'paper' => 'glossy',
            'border' => 'none',
            'rush' => 0,
            'payment' => 'counter',
            'product' => 'prints',
            'calendar_style' => 'wall',
            'lang' => 'el',
        ];
    }

    private static function empty(string $channel): array
    {
        return [
            'channel' => $channel === 'web' ? 'web' : 'kiosk',
            'photos' => [],
            'edits' => [],
            'options' => self::defaultOptions(),
        ];
    }

    private static function clamp(float $n): float
    {
        return max(0, min(1, $n));
    }

    private static function purgeUploads(string $sid): void
    {
        $root = (string) ($GLOBALS['lab_config']['uploads_dir'] ?? dirname(__DIR__) . '/data/uploads');
        $dir = $root . '/' . $sid;
        if (! is_dir($dir)) {
            return;
        }
        $files = glob($dir . '/*') ?: [];
        foreach ($files as $file) {
            if (is_file($file)) {
                @unlink($file);
            }
        }
        @rmdir($dir);
    }
}
