<?php

namespace App\Controllers\Api;

use App\Models\RewarderSpinLogModel;
use App\Models\SpinwheelModel;
use App\Models\SpinwheelSliceModel;
use App\Models\UserModel;
use CodeIgniter\HTTP\ResponseInterface;

class Spinwheels extends BaseApiController
{
    public function index(): ResponseInterface
    {
        $wheels = (new SpinwheelModel())
            ->where('status', 1)
            ->orderBy('id', 'desc')
            ->findAll();

        $items = array_values(array_map(static function ($w) {
            return (object) [
                'id' => (int) ($w->id ?? 0),
                'title' => (string) ($w->title ?? ''),
                'description' => (string) ($w->description ?? ''),
                'spin_cost' => (float) ($w->spin_cost ?? 0),
            ];
        }, $wheels ?: []));

        return $this->ok($items);
    }

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

        $wheel = (new SpinwheelModel())->where(['id' => $id, 'status' => 1])->first();
        if (!$wheel) {
            return $this->fail('Spin wheel not found.', 404);
        }

        $slices = (new SpinwheelSliceModel())
            ->where(['spinwheel_id' => $id, 'status' => 1])
            ->orderBy('sort_order', 'asc')
            ->findAll();

        $sliceOut = array_values(array_map(static function ($s) {
            $pictureUrl = '';
            if (!empty($s->picture_type)) {
                $pictureUrl = (string) url('uploads/spinwheel_slices/' . (int) ($s->id ?? 0) . '.' . $s->picture_type);
            }

            return (object) [
                'id' => (int) ($s->id ?? 0),
                'label' => (string) ($s->label ?? ''),
                'reward_value' => (string) ($s->reward_value ?? ''),
                'color' => (string) ($s->color ?? '#FF0000'),
                'probability_weight' => (int) ($s->probability_weight ?? 0),
                'picture_url' => $pictureUrl,
            ];
        }, $slices ?: []));

        return $this->ok((object) [
            'id' => (int) ($wheel->id ?? 0),
            'title' => (string) ($wheel->title ?? ''),
            'description' => (string) ($wheel->description ?? ''),
            'spin_cost' => (float) ($wheel->spin_cost ?? 0),
            'slices' => $sliceOut,
        ]);
    }

    public function spin($id = null): ResponseInterface
    {
        if (!function_exists('is_logged') || !is_logged()) {
            return $this->fail('Login required.', 401);
        }

        $spinwheelId = (int) $id;
        if ($spinwheelId < 1) {
            return $this->fail('Invalid spinwheel id.', 400);
        }

        $db = db_connect();
        if (!$db->tableExists('rewarder_spin_logs')) {
            return $this->fail('Spin logs table not found.', 500);
        }

        $wheel = (new SpinwheelModel())->where(['id' => $spinwheelId, 'status' => 1])->first();
        if (!$wheel) {
            return $this->fail('Spin wheel not found.', 404);
        }

        $spinCost = (int) round((float) ($wheel->spin_cost ?? 0));
        if ($spinCost < 0) {
            $spinCost = 0;
        }

        $userId = (int) logged('id');
        $userModel = new UserModel();
        $user = $userModel->select('id, total_reward_point')->where('id', $userId)->first();
        if (!$user) {
            return $this->fail('User not found.', 404);
        }

        $pointsBefore = (int) ($user->total_reward_point ?? 0);
        if ($spinCost > $pointsBefore) {
            return $this->fail('Invalid Rotation', 400, [
                'available_reward_point' => $pointsBefore,
                'spin_cost' => $spinCost,
            ]);
        }

        $data = $this->request->getJSON(true);
        $sliceId = is_array($data) ? (int) ($data['slice_id'] ?? 0) : 0;
        $sliceModel = new SpinwheelSliceModel();
        $picked = null;
        if ($sliceId > 0) {
            $picked = $sliceModel->where(['id' => $sliceId, 'spinwheel_id' => $spinwheelId, 'status' => 1])->first();
            if (!$picked) {
                return $this->fail('Invalid slice id.', 400);
            }
        } else {
            $slices = $sliceModel
                ->where(['spinwheel_id' => $spinwheelId, 'status' => 1])
                ->orderBy('sort_order', 'asc')
                ->findAll();
            if (empty($slices)) {
                return $this->fail('Spin wheel slices not found.', 404);
            }
            $picked = $this->pickSlice($slices);
            if (!$picked) {
                return $this->fail('Unable to pick spin result.', 500);
            }
        }

        $pointsAfter = max(0, $pointsBefore - $spinCost);
        $nowTs = date('Y-m-d H:i:s');

        $db->transStart();

        $db->table('users')
            ->where('id', $userId)
            ->set('total_reward_point', $pointsAfter)
            ->update();

        (new RewarderSpinLogModel())->insert([
            'user_id' => $userId,
            'spinwheel_id' => $spinwheelId,
            'slice_id' => (int) ($picked->id ?? 0),
            'reward_label' => (string) ($picked->label ?? ''),
            'reward_value' => (string) ($picked->reward_value ?? ''),
            'spin_cost' => $spinCost,
            'points_before' => $pointsBefore,
            'points_after' => $pointsAfter,
            'ip_address' => function_exists('ip_address') ? (string) ip_address() : '',
            'created_at' => $nowTs,
        ]);

        $db->transComplete();
        if ($db->transStatus() === false) {
            return $this->fail('Spin failed.', 500);
        }

        $pictureUrl = '';
        if (!empty($picked->picture_type)) {
            $pictureUrl = (string) url('uploads/spinwheel_slices/' . (int) ($picked->id ?? 0) . '.' . $picked->picture_type);
        }

        return $this->ok((object) [
            'spinwheel_id' => $spinwheelId,
            'slice' => (object) [
                'id' => (int) ($picked->id ?? 0),
                'label' => (string) ($picked->label ?? ''),
                'reward_value' => (string) ($picked->reward_value ?? ''),
                'color' => (string) ($picked->color ?? '#FF0000'),
                'probability_weight' => (int) ($picked->probability_weight ?? 0),
                'picture_url' => $pictureUrl,
            ],
            'spin_cost' => $spinCost,
            'points_before' => $pointsBefore,
            'points_after' => $pointsAfter,
        ]);
    }

    private function pickSlice(array $slices)
    {
        if (empty($slices)) {
            return null;
        }

        $weights = [];
        $sum = 0;
        foreach ($slices as $s) {
            $w = (int) ($s->probability_weight ?? 0);
            if ($w < 0) {
                $w = 0;
            }
            $weights[] = $w;
            $sum += $w;
        }

        if ($sum <= 0) {
            return $slices[random_int(0, count($slices) - 1)];
        }

        $r = random_int(1, $sum);
        $acc = 0;
        foreach ($slices as $i => $s) {
            $acc += $weights[$i];
            if ($r <= $acc) {
                return $s;
            }
        }

        return $slices[count($slices) - 1];
    }
}
