<?php

namespace App\Models;

use CodeIgniter\Model;

class MenuModel extends Model
{
    use \App\Models\Traits\Auditable;

    protected $table         = 'menu';
    protected $primaryKey    = 'mm_idx';
    protected $returnType    = 'object';
    protected $useTimestamps = false;
    protected $allowedFields = [
        'mt_idx', 'lg_idx', 'mm_name', 'mm_link', 'mm_pidx', 'mm_dep', 'mm_num', 'mm_cnode',
        'mm_level', 'mm_is_view',
    ];

    /**
     * 메뉴 종류·지자체별 전체 항목 (정렬: num)
     */
    public function getAllByType(int $mtIdx, int $lgIdx): array
    {
        return $this->where('mt_idx', $mtIdx)
            ->where('lg_idx', $lgIdx)
            ->orderBy('mm_num', 'ASC')
            ->findAll();
    }

    /**
     * 특정 mb_level에 노출할 메뉴만 필터링 (mm_is_view=Y, mm_level에 해당 레벨 포함 또는 빈값).
     * lg_idx 기준 해당 지자체 메뉴만 대상. super/본부(4·5)는 mm_level 무관하게 해당 지자체 메뉴 전체 노출.
     */
    public function getVisibleByLevel(int $mtIdx, int $mbLevel, int $lgIdx): array
    {
        $all = $this->getAllByType($mtIdx, $lgIdx);
        if (\Config\Roles::isSuperAdminEquivalent($mbLevel)) {
            return array_values(array_filter($all, static fn ($row) => (string) $row->mm_is_view === 'Y'));
        }
        $levelStr = (string) $mbLevel;
        $out = [];
        foreach ($all as $row) {
            if ((string) $row->mm_is_view !== 'Y') {
                continue;
            }
            if ($row->mm_level === '' || $row->mm_level === null) {
                $out[] = $row;
                continue;
            }
            $levels = array_map('trim', explode(',', (string) $row->mm_level));
            if (in_array($levelStr, $levels, true)) {
                $out[] = $row;
            }
        }
        return $out;
    }

    public function getItem(int $mmIdx): ?object
    {
        return $this->find($mmIdx);
    }

    /**
     * 하위 메뉴 개수
     */
    public function getChildCount(int $mmIdx): int
    {
        return $this->where('mm_pidx', $mmIdx)->countAllResults();
    }

    /**
     * 순서 변경 (mm_idx 배열 순서대로 mm_num 부여). 해당 지자체 소속 메뉴만 갱신.
     */
    public function setOrder(array $mmIdxList, int $lgIdx): void
    {
        foreach ($mmIdxList as $i => $mmIdx) {
            $row = $this->find((int) $mmIdx);
            if ($row && (int) $row->lg_idx === $lgIdx) {
                $this->update((int) $mmIdx, ['mm_num' => $i]);
            }
        }
    }

    /**
     * 순서 + 상위(부모)·깊이 변경을 함께 반영한다.
     * 하위 메뉴를 다른 상위 메뉴로 옮기는 드래그를 지원하기 위한 용도.
     *
     * @param array<int,array{idx:int,pidx:int,dep:int}> $items 화면 표시 순서대로 정렬된 구조 배열
     */
    public function setStructure(array $items, int $lgIdx): void
    {
        $affectedTypes = [];
        $i = 0;
        foreach ($items as $it) {
            $mmIdx = (int) ($it['idx'] ?? 0);
            if ($mmIdx <= 0) {
                continue;
            }
            $row = $this->find($mmIdx);
            if (! $row || (int) $row->lg_idx !== $lgIdx) {
                continue;
            }
            $this->update($mmIdx, [
                'mm_num'  => $i,
                'mm_pidx' => max(0, (int) ($it['pidx'] ?? 0)),
                'mm_dep'  => max(0, (int) ($it['dep'] ?? 0)),
            ]);
            $affectedTypes[(int) $row->mt_idx] = true;
            $i++;
        }
        // 상위가 바뀌었을 수 있으므로 종류별로 mm_cnode(자식 수)를 재계산
        foreach (array_keys($affectedTypes) as $mtIdx) {
            $this->recountChildNodes((int) $mtIdx, $lgIdx);
        }
    }

    /**
     * 특정 메뉴 종류·지자체의 각 메뉴에 대해 mm_cnode(직속 자식 수)를 실제 값으로 재계산한다.
     */
    private function recountChildNodes(int $mtIdx, int $lgIdx): void
    {
        $rows = $this->where('mt_idx', $mtIdx)->where('lg_idx', $lgIdx)->findAll();
        $childCount = [];
        foreach ($rows as $r) {
            $pid = (int) $r->mm_pidx;
            if ($pid > 0) {
                $childCount[$pid] = ($childCount[$pid] ?? 0) + 1;
            }
        }
        foreach ($rows as $r) {
            $want = (int) ($childCount[(int) $r->mm_idx] ?? 0);
            if ((int) $r->mm_cnode !== $want) {
                $this->update((int) $r->mm_idx, ['mm_cnode' => $want]);
            }
        }
    }

    /**
     * 추가 시 같은 레벨에서 mm_num 결정 (동일 지자체·메뉴종류·부모·깊이 기준)
     */
    public function getNextNum(int $mtIdx, int $lgIdx, int $mmPidx, int $mmDep): int
    {
        return $this->where('mt_idx', $mtIdx)
            ->where('lg_idx', $lgIdx)
            ->where('mm_pidx', $mmPidx)
            ->where('mm_dep', $mmDep)
            ->countAllResults();
    }

    /**
     * 해당 메뉴가 지정 지자체 소속인지 여부
     */
    public function belongsToLg(int $mmIdx, int $lgIdx): bool
    {
        $row = $this->select('mm_idx')->where('mm_idx', $mmIdx)->where('lg_idx', $lgIdx)->first();
        return $row !== null;
    }

    /**
     * 자식 있으면 삭제 불가
     */
    public function deleteSafe(int $mmIdx): array
    {
        $row = $this->find($mmIdx);
        if (! $row) {
            return ['ok' => false, 'msg' => '메뉴를 찾을 수 없습니다.'];
        }
        $childCount = $this->getChildCount($mmIdx);
        if ($childCount > 0) {
            return ['ok' => false, 'msg' => '하위 메뉴가 있으면 삭제할 수 없습니다.'];
        }
        $this->delete($mmIdx);
        if ((int) $row->mm_pidx > 0) {
            $this->updateCnode((int) $row->mm_pidx, -1);
        }
        return ['ok' => true];
    }

    public function updateCnode(int $mmPidx, int $delta): void
    {
        $row = $this->find($mmPidx);
        if (! $row) {
            return;
        }
        $newVal = max(0, (int) $row->mm_cnode + $delta);
        $this->update($mmPidx, ['mm_cnode' => $newVal]);
    }

    /**
     * 기본 지자체의 메뉴 구조를 다른 지자체로 복사.
     * mt_idx, srcLg, destLg 조합으로 이미 메뉴가 있으면 아무 작업도 하지 않는다.
     *
     * 기본 정책: srcLg(예: 1번 지자체)에 템플릿 메뉴가 있고,
     *           destLg(예: 남구청)에는 아직 메뉴가 없을 때 호출.
     */
    public function copyDefaultsFromLg(int $mtIdx, int $srcLg, int $destLg): void
    {
        if ($srcLg === $destLg) {
            return;
        }
        // 이미 대상 지자체에 메뉴가 있으면 복사하지 않음
        if ($this->where('mt_idx', $mtIdx)->where('lg_idx', $destLg)->countAllResults() > 0) {
            return;
        }

        // 원본 메뉴(트리 전체) 조회
        $source = $this->where('mt_idx', $mtIdx)
            ->where('lg_idx', $srcLg)
            ->orderBy('mm_dep', 'ASC')
            ->orderBy('mm_num', 'ASC')
            ->findAll();

        if (empty($source)) {
            return;
        }

        $idMap = [];

        foreach ($source as $row) {
            $oldId = (int) $row->mm_idx;
            $oldP  = (int) $row->mm_pidx;

            $newPidx = 0;
            if ($oldP > 0 && isset($idMap[$oldP])) {
                $newPidx = $idMap[$oldP];
            }

            $data = [
                'mt_idx'     => $mtIdx,
                'lg_idx'     => $destLg,
                'mm_name'    => $row->mm_name,
                'mm_link'    => $row->mm_link,
                'mm_pidx'    => $newPidx,
                'mm_dep'     => $row->mm_dep,
                'mm_num'     => $row->mm_num,
                'mm_cnode'   => $row->mm_cnode,
                'mm_level'   => $row->mm_level,
                'mm_is_view' => $row->mm_is_view,
            ];

            $this->insert($data);
            $newId = (int) $this->getInsertID();
            $idMap[$oldId] = $newId;
        }
    }

    /**
     * 특정 메뉴 타입(mt_idx)을 source 지자체 기준으로 모든 지자체에 재배포.
     * 기존 대상 지자체의 해당 타입 메뉴는 삭제 후 source 구조로 재생성한다.
     */
    public function syncTypeToAllLgs(int $mtIdx, int $sourceLg): void
    {
        if ($mtIdx <= 0 || $sourceLg <= 0) {
            return;
        }

        $source = $this->where('mt_idx', $mtIdx)
            ->where('lg_idx', $sourceLg)
            ->orderBy('mm_dep', 'ASC')
            ->orderBy('mm_num', 'ASC')
            ->findAll();
        if (empty($source)) {
            return;
        }

        $lgRows = $this->db->table('local_government')
            ->select('lg_idx')
            ->orderBy('lg_idx', 'ASC')
            ->get()
            ->getResultArray();

        foreach ($lgRows as $lgRow) {
            $destLg = (int) ($lgRow['lg_idx'] ?? 0);
            if ($destLg <= 0 || $destLg === $sourceLg) {
                continue;
            }

            $this->db->transStart();
            $this->where('mt_idx', $mtIdx)
                ->where('lg_idx', $destLg)
                ->delete();

            $idMap = [];
            foreach ($source as $row) {
                $oldId = (int) ($row->mm_idx ?? 0);
                $oldP = (int) ($row->mm_pidx ?? 0);
                $newPidx = 0;
                if ($oldP > 0 && isset($idMap[$oldP])) {
                    $newPidx = (int) $idMap[$oldP];
                }

                $this->insert([
                    'mt_idx' => $mtIdx,
                    'lg_idx' => $destLg,
                    'mm_name' => (string) ($row->mm_name ?? ''),
                    'mm_link' => (string) ($row->mm_link ?? ''),
                    'mm_pidx' => $newPidx,
                    'mm_dep' => (int) ($row->mm_dep ?? 0),
                    'mm_num' => (int) ($row->mm_num ?? 0),
                    'mm_cnode' => (int) ($row->mm_cnode ?? 0),
                    'mm_level' => (string) ($row->mm_level ?? ''),
                    'mm_is_view' => (string) ($row->mm_is_view ?? 'Y'),
                ]);
                $idMap[$oldId] = (int) $this->getInsertID();
            }
            $this->db->transComplete();
        }
    }

    /**
     * 재고 관리 하위 메뉴는 "재고 현황", "실사 선별 조회"만 유지.
     */
    public function pruneInventoryManagementMenus(int $mtIdx, int $lgIdx): void
    {
        if ($mtIdx <= 0 || $lgIdx <= 0) {
            return;
        }

        $parentRows = $this->where('mt_idx', $mtIdx)
            ->where('lg_idx', $lgIdx)
            ->where('mm_pidx', 0)
            ->groupStart()
                ->where('mm_name', '재고 관리')
                ->orWhere('mm_name', '재고관리')
            ->groupEnd()
            ->findAll();
        if ($parentRows === []) {
            return;
        }
        $parentIds = array_values(array_filter(array_map(
            static fn ($row): int => (int) ($row->mm_idx ?? 0),
            $parentRows
        )));
        if ($parentIds === []) {
            return;
        }

        $this->where('mt_idx', $mtIdx)
            ->where('lg_idx', $lgIdx)
            ->whereIn('mm_pidx', $parentIds)
            ->whereNotIn('mm_link', ['bag/inventory', 'bag/inventory/inspection-select'])
            ->delete();
    }

}
