<?php

namespace App\Controllers\Api;

use CodeIgniter\HTTP\ResponseInterface;

class ChallengeController extends BaseApiController
{
    protected $format = 'json';
    protected $table = 'challenge';

    /**
     * 챌린지 목록
     * GET /api/challenge/list
     */
    public function index()
    {
        $auth = $this->requireAuth();
        if ($auth instanceof ResponseInterface) {
            return $auth;
        }

        try {
            $page    = (int) ($this->request->getGet('page') ?? 1);
            $perPage = (int) ($this->request->getGet('per_page') ?? 10);
            if ($page < 1) $page = 1;
            if ($perPage < 1) $perPage = 10;
            $offset = ($page - 1) * $perPage;

            $search    = trim((string) $this->request->getGet('search'));
            $status    = trim((string) $this->request->getGet('status'));      // recruiting/running/ended
            $statusYN  = trim((string) $this->request->getGet('status_YN'));   // Y/N
            $startDate = trim((string) $this->request->getGet('start_date'));
            $endDate   = trim((string) $this->request->getGet('end_date'));

            $db = $this->getDB();
            $builder = $db->table($this->table);
            $builder->where('deleted_YN', 'N');

            if ($search !== '') {
                $builder->like('name', $search);
            }
            if ($statusYN === 'Y' || $statusYN === 'N') {
                $builder->where('status_YN', $statusYN);
            }

            // 상태 필터 (비노출 > 명시 마감 > end_date 경과 > 모집중 > 진행중 순위)
            $now = date('Y-m-d H:i:s');
            if ($status === 'hidden') {
                $builder->where('status_YN', 'N');
            } elseif ($status === 'ended') {
                $builder->where('status_YN', 'Y')
                    ->groupStart()
                        ->where('closed_at IS NOT NULL')
                        ->orWhere('end_date <', $now)
                    ->groupEnd();
            } elseif ($status === 'recruiting') {
                $builder->where('status_YN', 'Y')
                    ->where('closed_at IS NULL')
                    ->where('end_date >=', $now)
                    ->where('start_date >', $now);
            } elseif ($status === 'running') {
                $builder->where('status_YN', 'Y')
                    ->where('closed_at IS NULL')
                    ->where('end_date >=', $now)
                    ->where('start_date <=', $now);
            }

            // 등록일 기간 필터
            if (preg_match('/^\d{4}-\d{2}-\d{2}$/', $startDate)) {
                $builder->where('created_at >=', $startDate . ' 00:00:00');
            }
            if (preg_match('/^\d{4}-\d{2}-\d{2}$/', $endDate)) {
                $builder->where('created_at <=', $endDate . ' 23:59:59');
            }

            $total = $builder->countAllResults(false);

            // 상태/현재라운드는 SQL CASE+subquery로 계산 (escape 비활성화)
            $items = $builder
                ->select("
                    challenge.id, challenge.name, challenge.fee, challenge.start_date, challenge.end_date,
                    challenge.max_participants, challenge.total_rounds,
                    challenge.file_name, challenge.file_path, challenge.closed_at, challenge.closed_by,
                    challenge.status_YN, challenge.created_at,
                    CASE
                        WHEN challenge.status_YN = 'N' THEN 'hidden'
                        WHEN challenge.closed_at IS NOT NULL THEN 'ended'
                        WHEN challenge.end_date < NOW() THEN 'ended'
                        WHEN challenge.start_date > NOW() THEN 'recruiting'
                        ELSE 'running'
                    END AS derived_status,
                    CASE
                        WHEN challenge.closed_at IS NOT NULL THEN challenge.total_rounds
                        WHEN challenge.end_date < NOW() THEN challenge.total_rounds
                        ELSE COALESCE(
                            (SELECT MIN(cr.round_no) FROM challenge_round cr
                              WHERE cr.challenge_id = challenge.id AND cr.closed_at IS NULL),
                            challenge.total_rounds
                        )
                    END AS current_round
                ", false)
                ->orderBy('challenge.id', 'DESC')
                ->limit($perPage, $offset)
                ->get()
                ->getResult();

            // 상태별 카운트 (필터 무관 — 전체 기준, 우선순위 derived_status와 동일)
            $countAll = $db->table($this->table)->where('deleted_YN', 'N')->countAllResults();
            $countHidden = $db->table($this->table)
                ->where('deleted_YN', 'N')
                ->where('status_YN', 'N')
                ->countAllResults();
            $countRecruiting = $db->table($this->table)
                ->where('deleted_YN', 'N')
                ->where('status_YN', 'Y')
                ->where('closed_at IS NULL')
                ->where('end_date >=', $now)
                ->where('start_date >', $now)
                ->countAllResults();
            $countRunning = $db->table($this->table)
                ->where('deleted_YN', 'N')
                ->where('status_YN', 'Y')
                ->where('closed_at IS NULL')
                ->where('end_date >=', $now)
                ->where('start_date <=', $now)
                ->countAllResults();
            $countEnded = $db->table($this->table)
                ->where('deleted_YN', 'N')
                ->where('status_YN', 'Y')
                ->groupStart()
                    ->where('closed_at IS NOT NULL')
                    ->orWhere('end_date <', $now)
                ->groupEnd()
                ->countAllResults();

            return $this->respondSuccess([
                'items'       => $items,
                'total'       => $total,
                'page'        => $page,
                'per_page'    => $perPage,
                'total_pages' => (int) ceil($total / $perPage),
                'counts'      => [
                    'all'        => $countAll,
                    'recruiting' => $countRecruiting,
                    'running'    => $countRunning,
                    'ended'      => $countEnded,
                    'hidden'     => $countHidden,
                ],
            ]);
        } catch (\Exception $e) {
            log_message('error', 'ChallengeController index error: ' . $e->getMessage());
            return $this->respondError('목록 조회 중 오류가 발생했습니다: ' . $e->getMessage(), ResponseInterface::HTTP_INTERNAL_SERVER_ERROR);
        }
    }

    /**
     * 챌린지 상세 조회
     * GET /api/challenge/:id
     */
    public function show($id = null)
    {
        $auth = $this->requireAuth();
        if ($auth instanceof ResponseInterface) {
            return $auth;
        }

        if (empty($id)) {
            return $this->respondError('ID가 필요합니다.', ResponseInterface::HTTP_BAD_REQUEST);
        }

        try {
            $db = $this->getDB();

            // 1. 챌린지 기본 정보 + 상태 계산 (CASE 표현식 때문에 escape 비활성화)
            // detail은 시간 기반 상태만 — 노출 여부는 별도로 표시되므로 hidden 케이스 제외
            $row = $db->table($this->table)
                ->select("
                    id, name, fee, start_date, end_date, max_participants, total_rounds,
                    description, file_name, file_path, closed_at, closed_by,
                    status_YN, deleted_YN, created_at, updated_at,
                    CASE
                        WHEN closed_at IS NOT NULL THEN 'ended'
                        WHEN end_date < NOW() THEN 'ended'
                        WHEN start_date > NOW() THEN 'recruiting'
                        ELSE 'running'
                    END AS derived_status
                ", false)
                ->where('id', (int) $id)
                ->where('deleted_YN', 'N')
                ->get()
                ->getRow();

            if (!$row) {
                return $this->respondError('해당 챌린지를 찾을 수 없습니다.', ResponseInterface::HTTP_NOT_FOUND);
            }

            // 2. 라운드 목록
            $rounds = $db->table('challenge_round')
                ->where('challenge_id', (int) $id)
                ->orderBy('round_no', 'ASC')
                ->get()
                ->getResult();

            foreach ($rounds as $r) {
                if ($r->place_mode === 'all') {
                    // 라운드 단위 아이템
                    $r->items = $db->table('challenge_round_item cri')
                        ->select('cri.id, cri.item_id, i.name, i.type, i.point, i.file_name, i.file_path')
                        ->join('item i', 'i.id = cri.item_id', 'left')
                        ->where('cri.round_id', $r->id)
                        ->orderBy('cri.id', 'ASC')
                        ->get()
                        ->getResult();
                    $r->places = [];
                } else {
                    // specific — group_no로 묶어서 묶음(place) 단위로 반환
                    $placeRows = $db->table('challenge_round_place crp')
                        ->select("crp.id, crp.group_no, crp.place_type, crp.place_id,
                                  COALESCE(o.name, f.name) AS place_name", false)
                        ->join('onboard o', "o.id = crp.place_id AND crp.place_type = 'onboard'", 'left')
                        ->join('fishing f', "f.id = crp.place_id AND crp.place_type = 'fishing'", 'left')
                        ->where('crp.round_id', $r->id)
                        ->orderBy('crp.group_no', 'ASC')
                        ->orderBy('crp.id', 'ASC')
                        ->get()
                        ->getResult();

                    $groups = []; // group_no => place 묶음
                    foreach ($placeRows as $pr) {
                        $g = (int) $pr->group_no;
                        if (!isset($groups[$g])) {
                            // group 단위로 아이템 조회 (round_id + group_no)
                            $items = $db->table('challenge_round_group_item cgi')
                                ->select('cgi.id, cgi.item_id, i.name, i.type, i.point, i.file_name, i.file_path')
                                ->join('item i', 'i.id = cgi.item_id', 'left')
                                ->where('cgi.round_id', $r->id)
                                ->where('cgi.group_no', $g)
                                ->orderBy('cgi.id', 'ASC')
                                ->get()
                                ->getResult();

                            $groups[$g] = (object) [
                                'group_no' => $g,
                                'onboards' => [],
                                'items'    => $items,
                            ];
                        }
                        $groups[$g]->onboards[] = (object) [
                            'id'         => $pr->id,
                            'place_type' => $pr->place_type,
                            'place_id'   => $pr->place_id,
                            'place_name' => $pr->place_name,
                        ];
                    }

                    $r->places = array_values($groups);
                    $r->items = [];
                }
            }

            $row->rounds = $rounds;

            return $this->respondSuccess($row);
        } catch (\Exception $e) {
            log_message('error', 'ChallengeController show error: ' . $e->getMessage());
            return $this->respondError('조회 중 오류가 발생했습니다: ' . $e->getMessage(), ResponseInterface::HTTP_INTERNAL_SERVER_ERROR);
        }
    }

    /**
     * 챌린지 삭제 (soft delete)
     * DELETE /api/challenge/:id
     */
    public function delete($id = null)
    {
        $auth = $this->requireAuth();
        if ($auth instanceof ResponseInterface) {
            return $auth;
        }

        if (empty($id)) {
            return $this->respondError('ID가 필요합니다.', ResponseInterface::HTTP_BAD_REQUEST);
        }

        try {
            $db = $this->getDB();
            $exists = $db->table($this->table)
                ->where('id', (int) $id)->where('deleted_YN', 'N')->countAllResults();
            if ($exists === 0) {
                return $this->respondError('해당 챌린지를 찾을 수 없습니다.', ResponseInterface::HTTP_NOT_FOUND);
            }

            $db->table($this->table)->where('id', (int) $id)->update([
                'deleted_YN' => 'Y',
                'updated_at' => date('Y-m-d H:i:s'),
            ]);

            return $this->respondSuccess(null, '챌린지가 삭제되었습니다.');
        } catch (\Exception $e) {
            log_message('error', 'ChallengeController delete error: ' . $e->getMessage());
            return $this->respondError('삭제 중 오류가 발생했습니다: ' . $e->getMessage(), ResponseInterface::HTTP_INTERNAL_SERVER_ERROR);
        }
    }

    /**
     * 챌린지 등록
     * POST /api/challenge
     */
    public function create()
    {
        $auth = $this->requireAuth();
        if ($auth instanceof ResponseInterface) {
            return $auth;
        }

        try {
            $payload = $this->request->getJSON(true);
            if (!is_array($payload) || empty($payload)) {
                $payload = $this->request->getPost() ?? [];
            }

            $name             = trim((string) ($payload['name'] ?? ''));
            $fee              = trim((string) ($payload['fee'] ?? ''));
            $startDate        = trim((string) ($payload['start_date'] ?? ''));
            $endDate          = trim((string) ($payload['end_date'] ?? ''));
            $maxParticipants  = (int) ($payload['max_participants'] ?? 0);
            $description      = (string) ($payload['description'] ?? '');
            $status           = (($payload['status_YN'] ?? 'Y') === 'N') ? 'N' : 'Y';
            $rounds           = $payload['rounds'] ?? [];

            // ============================
            // 기본 필수값 검증
            // ============================
            if ($name === '') {
                return $this->respondError('챌린지명을 입력하세요.', ResponseInterface::HTTP_BAD_REQUEST);
            }
            if (mb_strlen($name) > 255) {
                return $this->respondError('챌린지명은 255자 이내로 입력하세요.', ResponseInterface::HTTP_BAD_REQUEST);
            }
            if ($fee === '') {
                return $this->respondError('참가비를 입력하세요.', ResponseInterface::HTTP_BAD_REQUEST);
            }
            if (!preg_match('/^\d{4}-\d{2}-\d{2}$/', $startDate)) {
                return $this->respondError('시작일을 올바르게 입력하세요.', ResponseInterface::HTTP_BAD_REQUEST);
            }
            if (!preg_match('/^\d{4}-\d{2}-\d{2}$/', $endDate)) {
                return $this->respondError('종료일을 올바르게 입력하세요.', ResponseInterface::HTTP_BAD_REQUEST);
            }
            if (strtotime($endDate) < strtotime($startDate)) {
                return $this->respondError('종료일은 시작일과 같거나 이후여야 합니다.', ResponseInterface::HTTP_BAD_REQUEST);
            }
            if ($maxParticipants < 100 || $maxParticipants > 999999) {
                return $this->respondError('최대 참가자 수는 100명 이상 999,999명 이하로 입력하세요.', ResponseInterface::HTTP_BAD_REQUEST);
            }

            // ============================
            // 라운드 검증
            // ============================
            if (!is_array($rounds) || count($rounds) < 2 || count($rounds) > 5) {
                return $this->respondError('라운드는 2~5개 사이여야 합니다.', ResponseInterface::HTTP_BAD_REQUEST);
            }

            foreach ($rounds as $idx => $r) {
                $no = $idx + 1;
                $placeMode = (string) ($r['place_mode'] ?? '');
                $qualified = (int) ($r['qualified'] ?? 0);

                if (!in_array($placeMode, ['all', 'specific'], true)) {
                    return $this->respondError("라운드 {$no}의 장소 모드를 선택하세요.", ResponseInterface::HTTP_BAD_REQUEST);
                }
                if ($qualified <= 0) {
                    return $this->respondError("라운드 {$no}의 진출자 수를 입력하세요.", ResponseInterface::HTTP_BAD_REQUEST);
                }

                if ($placeMode === 'specific') {
                    $places = $r['places'] ?? [];
                    if (!is_array($places) || count($places) === 0) {
                        return $this->respondError("라운드 {$no}에 장소를 1개 이상 추가하세요.", ResponseInterface::HTTP_BAD_REQUEST);
                    }
                    foreach ($places as $pIdx => $p) {
                        $pNo = $pIdx + 1;
                        $onboards = $p['onboards'] ?? [];
                        if (!is_array($onboards) || count($onboards) === 0) {
                            return $this->respondError("라운드 {$no} 장소 {$pNo}에 선상을 1개 이상 선택하세요.", ResponseInterface::HTTP_BAD_REQUEST);
                        }
                    }
                }
            }

            $db = $this->getDB();
            $db->transStart();

            // ============================
            // 1. challenge INSERT
            // ============================
            $challengeData = [
                'name'             => $name,
                'fee'              => $fee,
                'start_date'       => $startDate . ' 00:00:00',
                'end_date'         => $endDate . ' 23:59:59',
                'max_participants' => $maxParticipants,
                'total_rounds'     => count($rounds),
                'description'      => $description,
                'status_YN'        => $status,
                'deleted_YN'       => 'N',
                'created_at'       => date('Y-m-d H:i:s'),
            ];

            $db->table('challenge')->insert($challengeData);
            $challengeId = $db->insertID();

            if (!$challengeId) {
                $db->transRollback();
                return $this->respondError('챌린지 등록에 실패했습니다.', ResponseInterface::HTTP_INTERNAL_SERVER_ERROR);
            }

            // ============================
            // 2. 라운드 + 자식 테이블 INSERT
            // ============================
            foreach ($rounds as $idx => $r) {
                $roundNo   = $idx + 1;
                $placeMode = $r['place_mode'];
                $qualified = (int) $r['qualified'];

                $db->table('challenge_round')->insert([
                    'challenge_id' => $challengeId,
                    'round_no'     => $roundNo,
                    'place_mode'   => $placeMode,
                    'qualified'    => $qualified,
                ]);
                $roundId = $db->insertID();

                if ($placeMode === 'all') {
                    // 라운드 단위 아이템 (0개 허용)
                    $items = $r['items'] ?? [];
                    foreach ($items as $it) {
                        $itemId = (int) ($it['item_id'] ?? 0);
                        if ($itemId <= 0) continue;
                        $db->table('challenge_round_item')->insert([
                            'round_id' => $roundId,
                            'item_id'  => $itemId,
                        ]);
                    }
                } else {
                    // specific — 각 묶음(place)을 group_no로 식별
                    // 장소는 선상별로 분해, 아이템은 group 단위로 1번만 INSERT
                    $places = $r['places'] ?? [];
                    foreach ($places as $placeIdx => $p) {
                        $groupNo     = $placeIdx + 1;
                        $onboards    = $p['onboards'] ?? []; // [{type:'onboard'|'fishing', id:int}, ...]
                        $placeItems  = $p['items'] ?? [];

                        // 1. 묶음의 각 장소 INSERT (challenge_round_place)
                        foreach ($onboards as $sel) {
                            $placeType = (string) ($sel['type'] ?? 'onboard');
                            $placeId   = (int) ($sel['id'] ?? 0);
                            if ($placeId <= 0) continue;
                            if (!in_array($placeType, ['onboard', 'fishing'], true)) continue;

                            $db->table('challenge_round_place')->insert([
                                'round_id'   => $roundId,
                                'group_no'   => $groupNo,
                                'place_type' => $placeType,
                                'place_id'   => $placeId,
                            ]);
                        }

                        // 2. 묶음의 아이템 INSERT (challenge_round_group_item) — group 단위 1번만
                        foreach ($placeItems as $it) {
                            $itemId = (int) ($it['item_id'] ?? 0);
                            if ($itemId <= 0) continue;
                            $db->table('challenge_round_group_item')->insert([
                                'round_id' => $roundId,
                                'group_no' => $groupNo,
                                'item_id'  => $itemId,
                            ]);
                        }
                    }
                }
            }

            $db->transComplete();

            if ($db->transStatus() === false) {
                return $this->respondError('등록 중 데이터베이스 오류가 발생했습니다.', ResponseInterface::HTTP_INTERNAL_SERVER_ERROR);
            }

            // 결과 응답
            $row = $db->table('challenge')->where('id', $challengeId)->get()->getRow();
            return $this->respondSuccess($row, '챌린지가 등록되었습니다.', ResponseInterface::HTTP_CREATED);

        } catch (\Exception $e) {
            log_message('error', 'ChallengeController create error: ' . $e->getMessage());
            return $this->respondError('등록 중 오류가 발생했습니다: ' . $e->getMessage(), ResponseInterface::HTTP_INTERNAL_SERVER_ERROR);
        }
    }

    /**
     * 챌린지 수정
     * PUT /api/challenge/:id
     * 주의: end_date는 등록 후 수정 불가 (정책)
     * 자식 데이터(라운드/장소/아이템)는 모두 DELETE 후 재구성
     */
    public function update($id = null)
    {
        $auth = $this->requireAuth();
        if ($auth instanceof ResponseInterface) {
            return $auth;
        }

        if (empty($id)) {
            return $this->respondError('ID가 필요합니다.', ResponseInterface::HTTP_BAD_REQUEST);
        }

        try {
            $payload = $this->request->getJSON(true);
            if (!is_array($payload) || empty($payload)) {
                $payload = $this->request->getRawInput() ?? [];
            }

            $name             = trim((string) ($payload['name'] ?? ''));
            $fee              = trim((string) ($payload['fee'] ?? ''));
            $startDate        = trim((string) ($payload['start_date'] ?? ''));
            $endDate          = trim((string) ($payload['end_date'] ?? ''));
            $maxParticipants  = (int) ($payload['max_participants'] ?? 0);
            $description      = (string) ($payload['description'] ?? '');
            $status           = (($payload['status_YN'] ?? 'Y') === 'N') ? 'N' : 'Y';
            $rounds           = $payload['rounds'] ?? [];

            // 필수값 검증
            if ($name === '') return $this->respondError('챌린지명을 입력하세요.', ResponseInterface::HTTP_BAD_REQUEST);
            if (mb_strlen($name) > 255) return $this->respondError('챌린지명은 255자 이내로 입력하세요.', ResponseInterface::HTTP_BAD_REQUEST);
            if ($fee === '') return $this->respondError('참가비를 입력하세요.', ResponseInterface::HTTP_BAD_REQUEST);
            if (!preg_match('/^\d{4}-\d{2}-\d{2}$/', $startDate)) {
                return $this->respondError('시작일을 올바르게 입력하세요.', ResponseInterface::HTTP_BAD_REQUEST);
            }
            if (!preg_match('/^\d{4}-\d{2}-\d{2}$/', $endDate)) {
                return $this->respondError('종료일을 올바르게 입력하세요.', ResponseInterface::HTTP_BAD_REQUEST);
            }
            if (strtotime($endDate) < strtotime($startDate)) {
                return $this->respondError('종료일은 시작일과 같거나 이후여야 합니다.', ResponseInterface::HTTP_BAD_REQUEST);
            }
            if ($maxParticipants < 100 || $maxParticipants > 999999) {
                return $this->respondError('최대 참가자 수는 100명 이상 999,999명 이하로 입력하세요.', ResponseInterface::HTTP_BAD_REQUEST);
            }

            // 라운드 검증
            if (!is_array($rounds) || count($rounds) < 2 || count($rounds) > 5) {
                return $this->respondError('라운드는 2~5개 사이여야 합니다.', ResponseInterface::HTTP_BAD_REQUEST);
            }
            foreach ($rounds as $idx => $r) {
                $no = $idx + 1;
                $placeMode = (string) ($r['place_mode'] ?? '');
                $qualified = (int) ($r['qualified'] ?? 0);
                if (!in_array($placeMode, ['all', 'specific'], true)) {
                    return $this->respondError("라운드 {$no}의 장소 모드를 선택하세요.", ResponseInterface::HTTP_BAD_REQUEST);
                }
                if ($qualified <= 0) {
                    return $this->respondError("라운드 {$no}의 진출자 수를 입력하세요.", ResponseInterface::HTTP_BAD_REQUEST);
                }
                if ($placeMode === 'specific') {
                    $places = $r['places'] ?? [];
                    if (!is_array($places) || count($places) === 0) {
                        return $this->respondError("라운드 {$no}에 장소를 1개 이상 추가하세요.", ResponseInterface::HTTP_BAD_REQUEST);
                    }
                    foreach ($places as $pIdx => $p) {
                        $pNo = $pIdx + 1;
                        $onboards = $p['onboards'] ?? [];
                        if (!is_array($onboards) || count($onboards) === 0) {
                            return $this->respondError("라운드 {$no} 장소 {$pNo}에 선상/낚시터를 1개 이상 선택하세요.", ResponseInterface::HTTP_BAD_REQUEST);
                        }
                    }
                }
            }

            $db = $this->getDB();

            // 대상 존재 확인
            $exists = $db->table($this->table)
                ->where('id', (int) $id)->where('deleted_YN', 'N')->countAllResults();
            if ($exists === 0) {
                return $this->respondError('해당 챌린지를 찾을 수 없습니다.', ResponseInterface::HTTP_NOT_FOUND);
            }

            $db->transStart();

            // 1. challenge UPDATE
            $db->table('challenge')
                ->where('id', (int) $id)
                ->update([
                    'name'             => $name,
                    'fee'              => $fee,
                    'start_date'       => $startDate . ' 00:00:00',
                    'end_date'         => $endDate . ' 23:59:59',
                    'max_participants' => $maxParticipants,
                    'total_rounds'     => count($rounds),
                    'description'      => $description,
                    'status_YN'        => $status,
                    'updated_at'       => date('Y-m-d H:i:s'),
                ]);

            // 2. 기존 자식 데이터 모두 DELETE (CASCADE로 round_item, round_place, group_item 다 정리됨)
            $db->table('challenge_round')->where('challenge_id', (int) $id)->delete();

            // 3. 라운드 + 자식 INSERT (create 와 동일 로직)
            foreach ($rounds as $idx => $r) {
                $roundNo   = $idx + 1;
                $placeMode = $r['place_mode'];
                $qualified = (int) $r['qualified'];

                $db->table('challenge_round')->insert([
                    'challenge_id' => (int) $id,
                    'round_no'     => $roundNo,
                    'place_mode'   => $placeMode,
                    'qualified'    => $qualified,
                ]);
                $roundId = $db->insertID();

                if ($placeMode === 'all') {
                    $items = $r['items'] ?? [];
                    foreach ($items as $it) {
                        $itemId = (int) ($it['item_id'] ?? 0);
                        if ($itemId <= 0) continue;
                        $db->table('challenge_round_item')->insert([
                            'round_id' => $roundId,
                            'item_id'  => $itemId,
                        ]);
                    }
                } else {
                    $places = $r['places'] ?? [];
                    foreach ($places as $placeIdx => $p) {
                        $groupNo    = $placeIdx + 1;
                        $onboards   = $p['onboards'] ?? [];
                        $placeItems = $p['items'] ?? [];

                        foreach ($onboards as $sel) {
                            $placeType = (string) ($sel['type'] ?? 'onboard');
                            $placeId   = (int) ($sel['id'] ?? 0);
                            if ($placeId <= 0) continue;
                            if (!in_array($placeType, ['onboard', 'fishing'], true)) continue;

                            $db->table('challenge_round_place')->insert([
                                'round_id'   => $roundId,
                                'group_no'   => $groupNo,
                                'place_type' => $placeType,
                                'place_id'   => $placeId,
                            ]);
                        }

                        foreach ($placeItems as $it) {
                            $itemId = (int) ($it['item_id'] ?? 0);
                            if ($itemId <= 0) continue;
                            $db->table('challenge_round_group_item')->insert([
                                'round_id' => $roundId,
                                'group_no' => $groupNo,
                                'item_id'  => $itemId,
                            ]);
                        }
                    }
                }
            }

            $db->transComplete();

            if ($db->transStatus() === false) {
                return $this->respondError('수정 중 데이터베이스 오류가 발생했습니다.', ResponseInterface::HTTP_INTERNAL_SERVER_ERROR);
            }

            $row = $db->table('challenge')->where('id', (int) $id)->get()->getRow();
            return $this->respondSuccess($row, '챌린지가 수정되었습니다.');

        } catch (\Exception $e) {
            log_message('error', 'ChallengeController update error: ' . $e->getMessage());
            return $this->respondError('수정 중 오류가 발생했습니다: ' . $e->getMessage(), ResponseInterface::HTTP_INTERNAL_SERVER_ERROR);
        }
    }

    /**
     * 라운드 마감
     * POST /api/challenge/round/:round_id/close
     * 마지막 라운드 마감 시 challenge.closed_at 도 자동 설정 (자동 종료)
     */
    public function closeRound($roundId = null)
    {
        $auth = $this->requireAuth();
        if ($auth instanceof ResponseInterface) {
            return $auth;
        }

        if (empty($roundId)) {
            return $this->respondError('ID가 필요합니다.', ResponseInterface::HTTP_BAD_REQUEST);
        }

        try {
            $db = $this->getDB();
            $round = $db->table('challenge_round')
                ->where('id', (int) $roundId)->get()->getRow();
            if (!$round) {
                return $this->respondError('해당 라운드를 찾을 수 없습니다.', ResponseInterface::HTTP_NOT_FOUND);
            }
            if ($round->closed_at !== null) {
                return $this->respondError('이미 마감된 라운드입니다.', ResponseInterface::HTTP_BAD_REQUEST);
            }

            $now = date('Y-m-d H:i:s');
            $db->transStart();

            // 라운드 마감
            $db->table('challenge_round')
                ->where('id', (int) $roundId)
                ->update(['closed_at' => $now]);

            // 모든 라운드 마감되면 challenge.closed_at 자동 설정 (closed_by NULL = 자동)
            $remaining = $db->table('challenge_round')
                ->where('challenge_id', (int) $round->challenge_id)
                ->where('closed_at IS NULL')
                ->countAllResults();

            $challengeClosed = false;
            if ($remaining === 0) {
                $db->table('challenge')
                    ->where('id', (int) $round->challenge_id)
                    ->update([
                        'closed_at'  => $now,
                        'closed_by'  => null,
                        'updated_at' => $now,
                    ]);
                $challengeClosed = true;
            }

            $db->transComplete();

            if ($db->transStatus() === false) {
                return $this->respondError('마감 처리 중 오류가 발생했습니다.', ResponseInterface::HTTP_INTERNAL_SERVER_ERROR);
            }

            $msg = $challengeClosed
                ? '마지막 라운드가 마감되어 챌린지도 종료되었습니다.'
                : '라운드가 마감되었습니다.';
            return $this->respondSuccess(['challenge_closed' => $challengeClosed], $msg);
        } catch (\Exception $e) {
            log_message('error', 'ChallengeController closeRound error: ' . $e->getMessage());
            return $this->respondError('마감 중 오류가 발생했습니다: ' . $e->getMessage(), ResponseInterface::HTTP_INTERNAL_SERVER_ERROR);
        }
    }

    /**
     * 내 임시저장 조회
     * GET /api/challenge/draft
     */
    public function showDraft()
    {
        $auth = $this->requireAuth();
        if ($auth instanceof ResponseInterface) {
            return $auth;
        }

        try {
            $adminId = (int) $auth->admin_id;
            $row = $this->getDB()->table('challenge_draft')
                ->where('admin_id', $adminId)
                ->get()
                ->getRow();

            if (!$row) {
                return $this->respondSuccess(null, '임시저장 없음');
            }

            // data 컬럼은 JSON 문자열로 저장됐다고 가정
            $row->data = is_string($row->data) ? json_decode($row->data, true) : $row->data;

            return $this->respondSuccess($row, '임시저장 조회 성공');
        } catch (\Exception $e) {
            log_message('error', 'ChallengeController showDraft error: ' . $e->getMessage());
            return $this->respondError('조회 중 오류: ' . $e->getMessage(), ResponseInterface::HTTP_INTERNAL_SERVER_ERROR);
        }
    }

    /**
     * 임시저장 (upsert — 1인 1개)
     * POST /api/challenge/draft
     */
    public function saveDraft()
    {
        $auth = $this->requireAuth();
        if ($auth instanceof ResponseInterface) {
            return $auth;
        }

        try {
            $payload = $this->request->getJSON(true);
            if (!is_array($payload)) $payload = [];

            $adminId = (int) $auth->admin_id;
            $dataJson = json_encode($payload, JSON_UNESCAPED_UNICODE);
            $now = date('Y-m-d H:i:s');

            $db = $this->getDB();
            $existing = $db->table('challenge_draft')
                ->where('admin_id', $adminId)->get()->getRow();

            if ($existing) {
                $db->table('challenge_draft')
                    ->where('admin_id', $adminId)
                    ->update([
                        'data'       => $dataJson,
                        'updated_at' => $now,
                    ]);
            } else {
                $db->table('challenge_draft')->insert([
                    'admin_id'   => $adminId,
                    'data'       => $dataJson,
                    'created_at' => $now,
                ]);
            }

            return $this->respondSuccess(['updated_at' => $now], '임시저장 완료');
        } catch (\Exception $e) {
            log_message('error', 'ChallengeController saveDraft error: ' . $e->getMessage());
            return $this->respondError('임시저장 중 오류: ' . $e->getMessage(), ResponseInterface::HTTP_INTERNAL_SERVER_ERROR);
        }
    }

    /**
     * 임시저장 삭제
     * DELETE /api/challenge/draft
     */
    public function deleteDraft()
    {
        $auth = $this->requireAuth();
        if ($auth instanceof ResponseInterface) {
            return $auth;
        }

        try {
            $adminId = (int) $auth->admin_id;
            $this->getDB()->table('challenge_draft')
                ->where('admin_id', $adminId)
                ->delete();
            return $this->respondSuccess(null, '임시저장이 삭제되었습니다.');
        } catch (\Exception $e) {
            log_message('error', 'ChallengeController deleteDraft error: ' . $e->getMessage());
            return $this->respondError('삭제 중 오류: ' . $e->getMessage(), ResponseInterface::HTTP_INTERNAL_SERVER_ERROR);
        }
    }

    /**
     * 챌린지 타이틀 이미지 업로드 (교체)
     * POST /api/challenge/:id/image
     */
    public function uploadImage($id = null)
    {
        $auth = $this->requireAuth();
        if ($auth instanceof ResponseInterface) {
            return $auth;
        }

        if (empty($id)) {
            return $this->respondError('ID가 필요합니다.', ResponseInterface::HTTP_BAD_REQUEST);
        }

        try {
            $db = $this->getDB();
            $row = $db->table($this->table)
                ->where('id', (int) $id)->where('deleted_YN', 'N')->get()->getRow();
            if (!$row) {
                return $this->respondError('해당 챌린지를 찾을 수 없습니다.', ResponseInterface::HTTP_NOT_FOUND);
            }

            $file = $this->request->getFile('image');
            if (!$file || !$file->isValid()) {
                return $this->respondError('이미지가 전송되지 않았습니다.', ResponseInterface::HTTP_BAD_REQUEST);
            }
            $allowed = ['image/jpeg', 'image/png', 'image/gif', 'image/webp'];
            $mime = $file->getMimeType();
            if (!in_array($mime, $allowed, true)) {
                return $this->respondError('이미지 형식이 올바르지 않습니다.', ResponseInterface::HTTP_BAD_REQUEST);
            }

            $uploadPath = FCPATH . 'uploads/challenge/';
            if (!is_dir($uploadPath)) {
                mkdir($uploadPath, 0755, true);
            }
            $fileName = $file->getClientName();
            $stored = $file->getRandomName();
            $file->move($uploadPath, $stored);

            // 기존 이미지 삭제
            if (!empty($row->file_path)) {
                $oldFull = FCPATH . ltrim($row->file_path, '/');
                if (is_file($oldFull)) @unlink($oldFull);
            }

            $db->table($this->table)->where('id', (int) $id)->update([
                'file_name'  => $fileName,
                'file_path'  => '/uploads/challenge/' . $stored,
                'updated_at' => date('Y-m-d H:i:s'),
            ]);

            $updated = $db->table($this->table)->where('id', (int) $id)->get()->getRow();
            return $this->respondSuccess($updated, '이미지가 교체되었습니다.');
        } catch (\Exception $e) {
            log_message('error', 'ChallengeController uploadImage error: ' . $e->getMessage());
            return $this->respondError('이미지 업로드 중 오류가 발생했습니다: ' . $e->getMessage(), ResponseInterface::HTTP_INTERNAL_SERVER_ERROR);
        }
    }

    /**
     * 챌린지 타이틀 이미지 제거
     * DELETE /api/challenge/:id/image
     */
    public function deleteImage($id = null)
    {
        $auth = $this->requireAuth();
        if ($auth instanceof ResponseInterface) {
            return $auth;
        }

        if (empty($id)) {
            return $this->respondError('ID가 필요합니다.', ResponseInterface::HTTP_BAD_REQUEST);
        }

        try {
            $db = $this->getDB();
            $row = $db->table($this->table)
                ->where('id', (int) $id)->where('deleted_YN', 'N')->get()->getRow();
            if (!$row) {
                return $this->respondError('해당 챌린지를 찾을 수 없습니다.', ResponseInterface::HTTP_NOT_FOUND);
            }

            if (!empty($row->file_path)) {
                $full = FCPATH . ltrim($row->file_path, '/');
                if (is_file($full)) @unlink($full);
            }

            $db->table($this->table)->where('id', (int) $id)->update([
                'file_name'  => null,
                'file_path'  => null,
                'updated_at' => date('Y-m-d H:i:s'),
            ]);

            return $this->respondSuccess(null, '이미지가 제거되었습니다.');
        } catch (\Exception $e) {
            log_message('error', 'ChallengeController deleteImage error: ' . $e->getMessage());
            return $this->respondError('이미지 제거 중 오류가 발생했습니다: ' . $e->getMessage(), ResponseInterface::HTTP_INTERNAL_SERVER_ERROR);
        }
    }
}
