<?php

namespace App\Controllers\Api;

use CodeIgniter\HTTP\ResponseInterface;

class Shops extends BaseApiController
{
    public function index(): ResponseInterface
    {
        $cityId = (int) ($this->request->getGet('city_id') ?? 0);
        $cityQuery = trim((string) ($this->request->getGet('city') ?? ''));
        $categoryId = (int) ($this->request->getGet('category_id') ?? 0);
        $qText = trim((string) ($this->request->getGet('q') ?? ''));
        $page = (int) ($this->request->getGet('page') ?? 1);
        if ($page < 1) {
            $page = 1;
        }
        $perPage = (int) ($this->request->getGet('per_page') ?? 24);
        if ($perPage < 1) {
            $perPage = 24;
        }
        if ($perPage > 100) {
            $perPage = 100;
        }
        $offset = ($page - 1) * $perPage;

        $filterCityIds = $this->resolveCityFilterIds($cityId, $cityQuery);

        $countQ = db_connect()->table('rewarder_shops sh')
            ->join('rewarder_shop_categories cat', 'cat.id = sh.category_id', 'left')
            ->where('sh.status', 1);

        if (!empty($filterCityIds)) {
            $countQ->whereIn('sh.city_id', $filterCityIds);
        } elseif ($cityQuery !== '') {
            $countQ->where('1=0', null, false);
        }

        if ($categoryId > 0) {
            $countQ->where('sh.category_id', $categoryId);
        }

        if ($qText !== '') {
            $countQ->groupStart()
                ->like('sh.name', $qText)
                ->orLike('sh.address', $qText)
                ->orLike('cat.name', $qText)
            ->groupEnd();
        }

        $total = (int) $countQ->countAllResults();

        $q = db_connect()->table('rewarder_shops sh')
            ->select('sh.*, c.name AS city_name, cat.name AS category_name')
            ->join('rewarder_cities c', 'c.id = sh.city_id', 'left')
            ->join('rewarder_shop_categories cat', 'cat.id = sh.category_id', 'left')
            ->where('sh.status', 1);

        if (!empty($filterCityIds)) {
            $q->whereIn('sh.city_id', $filterCityIds);
        } elseif ($cityQuery !== '') {
            $q->where('1=0', null, false);
        }

        if ($categoryId > 0) {
            $q->where('sh.category_id', $categoryId);
        }

        if ($qText !== '') {
            $q->groupStart()
                ->like('sh.name', $qText)
                ->orLike('sh.address', $qText)
                ->orLike('cat.name', $qText)
            ->groupEnd();
        }

        $shops = $q->orderBy('RAND()', '', false)->limit($perPage, $offset)->get()->getResult();

        $shopDiscounts = $this->loadActiveDiscountsForShops($shops);

        $items = array_values(array_map(function ($s) use ($shopDiscounts) {
            $img = !empty($s->img_type)
                ? urlUpload('shops/' . (int) $s->id . '.' . $s->img_type, false)
                : admin_assets('img/default-150x150.png');

            $offers = $shopDiscounts[(int) $s->id] ?? [];
            $offersOut = array_values(array_map([$this, 'normalizeDiscount'], $offers));

            return (object) [
                'id' => (int) $s->id,
                'name' => (string) ($s->name ?? ''),
                'slug' => (string) ($s->slug ?? ''),
                'phone' => (string) ($s->phone ?? ''),
                'address' => (string) ($s->address ?? ''),
                'city_id' => (int) ($s->city_id ?? 0),
                'city_name' => (string) ($s->city_name ?? ''),
                'category_id' => (int) ($s->category_id ?? 0),
                'category_name' => (string) ($s->category_name ?? ''),
                'image_url' => (string) $img,
                'offers' => $offersOut,
            ];
        }, $shops));

        $totalPages = (int) ceil($total / $perPage);
        if ($totalPages < 1) {
            $totalPages = 1;
        }
        if ($page > $totalPages) {
            $page = $totalPages;
        }

        return $this->ok($items, [
            'page' => $page,
            'per_page' => $perPage,
            'total' => $total,
            'total_pages' => $totalPages,
        ]);
    }

    public function show($id = null): ResponseInterface
    {
        $id = (int) $id;
        if ($id < 1) {
            return $this->fail('Invalid shop id.', 400);
        }

        $shop = db_connect()->table('rewarder_shops sh')
            ->select('sh.*, c.name AS city_name, cat.name AS category_name')
            ->join('rewarder_cities c', 'c.id = sh.city_id', 'left')
            ->join('rewarder_shop_categories cat', 'cat.id = sh.category_id', 'left')
            ->where('sh.id', $id)
            ->where('sh.status', 1)
            ->get()
            ->getRow();

        if (!$shop) {
            return $this->fail('Shop not found.', 404);
        }

        $img = !empty($shop->img_type)
            ? urlUpload('shops/' . (int) $shop->id . '.' . $shop->img_type, false)
            : admin_assets('img/default-150x150.png');

        $discounts = $this->loadActiveDiscountsForShop((int) $shop->id);

        return $this->ok((object) [
            'id' => (int) $shop->id,
            'name' => (string) ($shop->name ?? ''),
            'slug' => (string) ($shop->slug ?? ''),
            'phone' => (string) ($shop->phone ?? ''),
            'address' => (string) ($shop->address ?? ''),
            'city_id' => (int) ($shop->city_id ?? 0),
            'city_name' => (string) ($shop->city_name ?? ''),
            'category_id' => (int) ($shop->category_id ?? 0),
            'category_name' => (string) ($shop->category_name ?? ''),
            'image_url' => (string) $img,
            'discounts' => array_values(array_map([$this, 'normalizeDiscount'], $discounts)),
        ]);
    }

    private function resolveCityFilterIds(int $cityId, string $cityQuery): array
    {
        if ($cityId > 0) {
            return [$cityId];
        }

        if ($cityQuery === '') {
            return [];
        }

        $searchCityTerm = $cityQuery;
        if (strpos($searchCityTerm, '-') !== false) {
            $searchCityTerm = trim((string) explode('-', $searchCityTerm, 2)[0]);
        }
        if (strpos($searchCityTerm, ',') !== false) {
            $searchCityTerm = trim((string) explode(',', $searchCityTerm, 2)[0]);
        }
        if ($searchCityTerm === '') {
            $searchCityTerm = $cityQuery;
        }

        $rows = db_connect()->table('rewarder_cities')
            ->select('id')
            ->where('status', 1)
            ->like('name', $searchCityTerm)
            ->limit(200)
            ->get()
            ->getResult();

        return array_values(array_map(static fn ($r) => (int) $r->id, $rows));
    }

    private function loadActiveDiscountsForShops(array $shops): array
    {
        if (empty($shops)) {
            return [];
        }

        $shopIds = array_values(array_map(static fn ($r) => (int) $r->id, $shops));
        $now = date('Y-m-d H:i:s');

        $drows = db_connect()->table('rewarder_discounts d')
            ->select('d.*')
            ->whereIn('d.shop_id', $shopIds)
            ->where('d.status', 1)
            ->groupStart()
                ->where('d.start_at', null)->orWhere('d.start_at <=', $now)
            ->groupEnd()
            ->groupStart()
                ->where('d.end_at', null)->orWhere('d.end_at >=', $now)
            ->groupEnd()
            ->orderBy('d.id', 'desc')
            ->get()
            ->getResult();

        if (empty($drows)) {
            return [];
        }

        $resolved = $this->resolveDiscountScopeLabels($drows);

        $shopDiscounts = [];
        foreach ($resolved as $dr) {
            $sid = (int) $dr->shop_id;
            if (!isset($shopDiscounts[$sid])) {
                $shopDiscounts[$sid] = [];
            }
            if (count($shopDiscounts[$sid]) < 3) {
                $shopDiscounts[$sid][] = $dr;
            }
        }

        return $shopDiscounts;
    }

    private function loadActiveDiscountsForShop(int $shopId): array
    {
        $now = date('Y-m-d H:i:s');

        $drows = db_connect()->table('rewarder_discounts d')
            ->select('d.*')
            ->where('d.shop_id', $shopId)
            ->where('d.status', 1)
            ->groupStart()
                ->where('d.start_at', null)->orWhere('d.start_at <=', $now)
            ->groupEnd()
            ->groupStart()
                ->where('d.end_at', null)->orWhere('d.end_at >=', $now)
            ->groupEnd()
            ->orderBy('d.id', 'desc')
            ->get()
            ->getResult();

        return $this->resolveDiscountScopeLabels($drows);
    }

    private function resolveDiscountScopeLabels(array $discounts): array
    {
        if (empty($discounts)) {
            return [];
        }

        $idBuckets = [
            'PRODUCT' => [],
            'PRODUCT_CATEGORY' => [],
            'SERVICE' => [],
            'SERVICE_CATEGORY' => [],
        ];

        foreach ($discounts as $dr) {
            $scope = (string) ($dr->applies_to ?? '');
            $tid = $dr->target_id ?? null;
            if ($tid && isset($idBuckets[$scope])) {
                $idBuckets[$scope][] = (int) $tid;
            }
        }

        $lookups = [
            'PRODUCT' => [],
            'PRODUCT_CATEGORY' => [],
            'SERVICE' => [],
            'SERVICE_CATEGORY' => [],
        ];

        if (!empty($idBuckets['PRODUCT'])) {
            $rows = db_connect()->table('rewarder_products')
                ->select('id, name')
                ->whereIn('id', array_unique($idBuckets['PRODUCT']))
                ->get()
                ->getResult();
            $lookups['PRODUCT'] = array_column($rows, 'name', 'id');
        }
        if (!empty($idBuckets['PRODUCT_CATEGORY'])) {
            $rows = db_connect()->table('rewarder_product_categories')
                ->select('id, name')
                ->whereIn('id', array_unique($idBuckets['PRODUCT_CATEGORY']))
                ->get()
                ->getResult();
            $lookups['PRODUCT_CATEGORY'] = array_column($rows, 'name', 'id');
        }
        if (!empty($idBuckets['SERVICE'])) {
            $rows = db_connect()->table('rewarder_services')
                ->select('id, name')
                ->whereIn('id', array_unique($idBuckets['SERVICE']))
                ->get()
                ->getResult();
            $lookups['SERVICE'] = array_column($rows, 'name', 'id');
        }
        if (!empty($idBuckets['SERVICE_CATEGORY'])) {
            $rows = db_connect()->table('rewarder_service_categories')
                ->select('id, name')
                ->whereIn('id', array_unique($idBuckets['SERVICE_CATEGORY']))
                ->get()
                ->getResult();
            $lookups['SERVICE_CATEGORY'] = array_column($rows, 'name', 'id');
        }

        foreach ($discounts as $dr) {
            $scope = (string) ($dr->applies_to ?? 'ALL');
            if ($scope === 'ALL') {
                $dr->scope_label = 'All items/services';
                continue;
            }

            $tid = $dr->target_id ?? null;
            $name = ($tid && isset($lookups[$scope])) ? ($lookups[$scope][(int) $tid] ?? null) : null;
            $dr->scope_label = $name ? (string) $name : ('#' . (string) ($tid ?: ''));
        }

        return $discounts;
    }

    private function normalizeDiscount($d): object
    {
        $dtype = (string) ($d->discount_type ?? 'PERCENT');
        $val = (float) ($d->value ?? 0);
        $label = $dtype === 'PERCENT'
            ? (rtrim(rtrim(number_format($val, 2), '0'), '.') . '% off')
            : ('₹' . number_format($val, 2) . ' off');

        $scopeLabel = (string) ($d->scope_label ?? '');
        if ($scopeLabel === '') {
            $scope = (string) ($d->applies_to ?? 'ALL');
            $scopeLabel = $scope === 'ALL' ? 'All items/services' : $scope;
        }

        return (object) [
            'id' => (int) ($d->id ?? 0),
            'shop_id' => (int) ($d->shop_id ?? 0),
            'applies_to' => (string) ($d->applies_to ?? 'ALL'),
            'target_id' => $d->target_id !== null ? (int) $d->target_id : null,
            'discount_type' => $dtype,
            'value' => $val,
            'min_amount' => $d->min_amount !== null ? (float) $d->min_amount : null,
            'start_at' => $d->start_at ?? null,
            'end_at' => $d->end_at ?? null,
            'label' => $label,
            'scope_label' => $scopeLabel,
        ];
    }
}
