<script setup lang="ts">
import { ref, onMounted, watch, computed } from "vue";
import { commonStore } from "@/stores/commonStore";
import { storage } from "@/utils/storage.js";
import { ProjectService } from "@/components/service/project/projectService";
import { UserManagerService } from "@/components/service/management/UserManagerService";
import type { Permission } from "@/components/models/project/Project";
import IconDeleteBtn from "@/components/atoms/button/IconDeleteBtn.vue";
import IconModifyBtn from "@/components/atoms/button/IconModifyBtn.vue";

const store = commonStore();

type SearchType = "전체" | "제목";

const searchOptions = [
  { label: "전체", value: "전체" as SearchType },
  { label: "제목", value: "제목" as SearchType },
];

const SEARCH_TYPE_MAP: Record<SearchType | "", "ALL" | "TITLE"> = {
  "": "ALL",
  전체: "ALL",
  제목: "TITLE",
};

const DEFAULT_PERMISSIONS: Permission[] = [
  "CREATE",
  "READ",
  "UPDATE",
  "DELETE",
];

const roles = ref<string[]>([]);
const refreshRoles = () => {
  const auth = storage.getAuth?.() ?? storage.get?.("vpp-Auth") ?? null;
  const r = auth?.userInfo?.roles ?? auth?.roles ?? [];
  roles.value = Array.isArray(r) ? r : [];
};
const isAdmin = computed(() => roles.value.includes("ROLE_ADMIN"));

const tableHeader = [
  { label: "No", width: "5%", style: "word-break: keep-all;" },
  { label: "Project Name", width: "20%", style: "word-break: keep-all;" },
  { label: "Description", width: "10%", style: "word-break: keep-all;" },
  { label: "Select Users", width: "12%", style: "word-break: keep-all;" },
  { label: "Created DateTime", width: "18%", style: "word-break: keep-all;" },
  { label: "Action", width: "13%", style: "word-break: keep-all;" },
];

const pageSizeOptions = [
  { text: "10 페이지", value: 10 },
  { text: "50 페이지", value: 50 },
  { text: "100 페이지", value: 100 },
];

type Row = {
  no: number;
  name: string;
  desc: string;
  users: string[]; // 목록 표시용(= mod_user_nm 우선)
  registDt: string;
  deviceKey: number;
};

// 원본 reg 값 보관(수정 시 그대로 유지)
const projectRegById = ref<Record<number, { regId?: string; regNm?: string }>>(
  {},
);

const data = ref({
  params: {
    pageNum: 1,
    pageSize: 10,
    searchType: "전체" as SearchType,
    searchText: "",
  },
  results: [] as Row[],
  totalDataLength: 0,
  pageLength: 0,
  modalMode: "" as "create" | "edit" | "",
  selectedData: null as Row | null,
  allSelected: false,
  selected: [] as Array<{ deviceKey: number }>,
  isCreateVisible: false,
  isUploadVisible: false,
  isModalVisible: false,
  isConfirmDialogVisible: false,
});

const fmtDate = (v?: string) => (v ? v.replace("T", " ").slice(0, 19) : "-");
const splitCsv = (v?: string) =>
  String(v ?? "")
    .split(",")
    .map((s) => s.trim())
    .filter(Boolean);

type UserOption = { id: number | string; username: string };
const userOptions = ref<UserOption[]>([]);
async function loadUsers() {
  const { data } = await UserManagerService.getAll();
  const raw = data as Array<{ id: number | string; username: string }>;
  userOptions.value = raw.map((u) => ({ id: u.id, username: u.username }));
}

// 서버 → UI 한 줄 변환 (mod_user_nm 우선)
function toRow(p: any, no: number): Row {
  let displayNm = "";
  if (typeof p.modUserNm === "string" && p.modUserNm.length > 0)
    displayNm = p.modUserNm;
  else if (typeof p.regUserNm === "string") displayNm = p.regUserNm;

  // 원본 reg 보관
  projectRegById.value[p.id] = { regId: p.regUserId, regNm: p.regUserNm };

  return {
    no,
    name: p.prjNm,
    desc: p.prjDesc,
    users: splitCsv(displayNm),
    registDt: fmtDate(p.prjStartDt), // 그대로 사용
    deviceKey: p.id,
  };
}

/** ===== 목록 조회 (검색/페이지네이션 포함) ===== */
async function getData() {
  const { pageNum, pageSize, searchType, searchText } = data.value.params;

  const mapped = SEARCH_TYPE_MAP[searchType] || "ALL";
  const keyword = (searchText || "").trim();

  const needLocalFilter = mapped !== "ALL" && keyword.length > 0;

  let reqPage = data.value.params.pageNum;
  let reqSize = data.value.params.pageSize;
  if (needLocalFilter) {
    reqPage = 0;
    reqSize = 1000;
  }

  const payload = {
    page: reqPage,
    size: reqSize,
    keyword,
    searchType: mapped,
    sortField: "id",
    sortDirection: "DESC",
  };

  try {
    const res = await ProjectService.searchProjects(payload as any);
    const result = res?.data ?? res;
    let list: any[] = result?.content ?? (Array.isArray(result) ? result : []);

    // 로컬 필터
    if (needLocalFilter) {
      const kw = keyword.toLowerCase();
      list = list.filter((x: any) =>
        String(x?.prjNm ?? "")
          .toLowerCase()
          .includes(kw),
      );
      const uiSize = data.value.params.pageSize;
      const totalElements = list.length;
      const totalPages = Math.max(1, Math.ceil(totalElements / uiSize));
      const safePage = Math.min(Math.max(1, pageNum), totalPages);
      const start = (safePage - 1) * uiSize;
      const pageSlice = list.slice(start, start + uiSize);

      const firstNo = totalElements - start;

      // ★ 새로 그릴 때 reg 원본 맵도 갱신
      projectRegById.value = {};
      data.value.results = pageSlice.map((p: any, i: number) =>
        toRow(p, Math.max(1, firstNo - i)),
      );
      data.value.totalDataLength = totalElements;
      data.value.pageLength = totalPages;
      return;
    }

    const totalElements =
      typeof result?.totalElements === "number"
        ? result.totalElements
        : (list.length ?? 0);

    const serverPage = result?.pageable?.pageNumber;
    const serverSize = result?.pageable?.pageSize;
    const offset =
      typeof result?.pageable?.offset === "number"
        ? result.pageable.offset
        : typeof serverPage === "number" && typeof serverSize === "number"
          ? serverPage * serverSize
          : (pageNum - 1) * pageSize;

    const firstNo = totalElements - offset;

    // ★ 새로 그릴 때 reg 원본 맵도 갱신
    projectRegById.value = {};
    data.value.results = list.map((p: any, i: number) =>
      toRow(p, Math.max(1, firstNo - i)),
    );

    data.value.totalDataLength = totalElements;
    data.value.pageLength =
      typeof result?.totalPages === "number"
        ? result.totalPages
        : Math.max(1, Math.ceil(totalElements / pageSize));
  } catch (e) {
    console.error("[Project] search error:", e);
    data.value.results = [];
    data.value.totalDataLength = 0;
    data.value.pageLength = 1;
  }
}

/** 트리거 */
function doSearch() {
  data.value.params.pageNum = 1;
  getData();
}
function changePageSize(size: number) {
  data.value.params.pageSize = size;
  data.value.params.pageNum = 1;
  getData();
}
function changePageNum(page: number) {
  data.value.params.pageNum = page;
  getData();
}
watch(
  () => data.value.params.searchType,
  () => doSearch(),
);

/** ===== 생성/수정 다이얼로그 ===== */
const form = ref({
  prjCd: "",
  prjNm: "",
  prjDesc: "",
  selectedUsers: [] as string[],
});
const editingProjectId = ref<number | null>(null);

const resetForm = () => {
  form.value.prjCd = `PRJ${Date.now()}`;
  form.value.prjNm = "";
  form.value.prjDesc = "";
  form.value.selectedUsers = [];
};

// 생성 payload (mod* 안 보냄)
type NewProjectPayload = {
  id: null;
  prjCd: string;
  prjNm: string;
  prjDesc: string;
  prjStartDt: string;
  prjEndDt: string;
  delYn: string;
  regDate: string;
  regUserId?: string;
  regUserNm?: string;
};
// 수정 payload (reg* 유지, mod* 반영)
type UpdateProjectPayload = {
  id: number;
  prjCd: string;
  prjNm: string;
  prjDesc: string;
  prjStartDt: string;
  prjEndDt: string;
  delYn: string;
  regDate: string;
  regUserId?: string;
  regUserNm?: string;
  modDate: string;
  modUserId?: string;
  modUserNm?: string;
};

function buildCreatePayload(): NewProjectPayload {
  const today = new Date().toISOString().slice(0, 10);
  const nowIso = new Date().toISOString();

  const names = form.value.selectedUsers;
  const namesCsv = names.join(",");
  const idsCsv = names
    .map((name) => userOptions.value.find((u) => u.username === name)?.id)
    .filter(
      (v): v is number | string => v !== undefined && v !== null && v !== "",
    )
    .map(String)
    .join(",");

  return {
    id: null,
    prjCd: form.value.prjCd,
    prjNm: form.value.prjNm,
    prjDesc: form.value.prjDesc,
    prjStartDt: today,
    prjEndDt: today,
    delYn: "N",
    regDate: nowIso,
    regUserId: idsCsv,
    regUserNm: namesCsv,
  };
}

function buildUpdatePayload(): UpdateProjectPayload {
  const today = new Date().toISOString().slice(0, 10);
  const nowIso = new Date().toISOString();

  const names = form.value.selectedUsers;
  const namesCsv = names.join(",");
  const idsCsv = names
    .map((name) => userOptions.value.find((u) => u.username === name)?.id)
    .filter(
      (v): v is number | string => v !== undefined && v !== null && v !== "",
    )
    .map(String)
    .join(",");

  const id = editingProjectId.value!;
  const kept = projectRegById.value[id] || {};

  return {
    id,
    prjCd: form.value.prjCd,
    prjNm: form.value.prjNm,
    prjDesc: form.value.prjDesc,
    prjStartDt: today,
    prjEndDt: today,
    delYn: "N",
    regDate: nowIso,
    regUserId: kept.regId,
    regUserNm: kept.regNm,
    modDate: nowIso,
    modUserId: idsCsv,
    modUserNm: namesCsv,
  };
}

async function grantDefaultPermissions(projectId: number, usernames: string[]) {
  if (!usernames?.length) return;
  const set = new Set(usernames);
  const numericIds = userOptions.value
    .filter((u) => set.has(u.username))
    .map((u) => Number(u.id))
    .filter((n) => Number.isFinite(n));

  await Promise.all(
    numericIds.map((uid) =>
      ProjectService.projectAuthority(projectId, {
        projectId,
        userId: uid,
        permissions: DEFAULT_PERMISSIONS,
      }),
    ),
  );
}

async function saveProject() {
  try {
    let projectId: number;

    if (data.value.modalMode === "create") {
      const payload = buildCreatePayload();
      const res = await ProjectService.add(payload);
      projectId = res.data.id;
    } else {
      const payload = buildUpdatePayload();
      await ProjectService.update(editingProjectId.value!, payload);
      projectId = editingProjectId.value!;
    }

    await grantDefaultPermissions(projectId, form.value.selectedUsers);
    await getData();
    data.value.isCreateVisible = false;
  } catch (e) {
    console.error(`${data.value.modalMode} 실패:`, e);
  }
}

function openCreateModal() {
  data.value.modalMode = "create";
  editingProjectId.value = null;
  resetForm();
  data.value.isCreateVisible = true;
}
function openEditModal(row: Row) {
  data.value.modalMode = "edit";
  editingProjectId.value = row.deviceKey;
  form.value.prjCd = row.name;
  form.value.prjNm = row.name;
  form.value.prjDesc = row.desc === "-" ? "" : row.desc;
  form.value.selectedUsers = Array.isArray(row.users) ? [...row.users] : [];

  // 선택 목록에 없는 사용자도 보이도록 보정
  const known = new Set(userOptions.value.map((u) => u.username));
  const missing = form.value.selectedUsers
    .filter((u) => !known.has(u))
    .map((u) => ({ id: u, username: u }));
  if (missing.length) userOptions.value = [...userOptions.value, ...missing];

  data.value.isCreateVisible = true;
}

function getSelectedAllData() {
  data.value.selected = data.value.allSelected
    ? data.value.results.map((r) => ({ deviceKey: r.deviceKey }))
    : [];
}

async function deleteRows(targetList?: Array<{ deviceKey: number }>) {
  const removeList = targetList ?? data.value.selected;
  if (!removeList?.length) return;

  const ids = removeList.map((x) => x.deviceKey);
  const remove = (id: number) =>
    ProjectService.delete(id).then((res) => {
      if (res.status < 200 || res.status >= 300) return Promise.reject(res);
    });

  const after = async () => {
    if (
      ids.length >= data.value.results.length &&
      data.value.params.pageNum > 1
    ) {
      data.value.params.pageNum -= 1;
    }
    await getData();
    data.value.isConfirmDialogVisible = false;
    data.value.selected = [];
    data.value.allSelected = false;
  };

  if (ids.length === 1) {
    try {
      await remove(ids[0]);
      store.setSnackbarMsg?.({
        color: "success",
        text: "삭제되었습니다.",
        result: 200,
      });
    } catch (err) {
      store.setSnackbarMsg?.({
        color: "warning",
        text: "삭제 실패",
        result: 500,
      });
      console.error(err);
    } finally {
      after();
    }
  } else {
    Promise.all(ids.map(remove))
      .then(() =>
        store.setSnackbarMsg?.({
          color: "success",
          text: "모두 삭제되었습니다.",
          result: 200,
        }),
      )
      .catch((err) => {
        store.setSnackbarMsg?.({
          color: "warning",
          text: "일부 삭제 실패",
          result: 500,
        });
        console.error(err);
      })
      .finally(after);
  }
}

watch(
  () => data.value.isCreateVisible,
  (now, prev) => {
    if (prev && !now) getData();
  },
);

onMounted(async () => {
  refreshRoles();
  await Promise.all([loadUsers(), getData()]);
});
</script>

<template>
  <div class="w-100">
    <v-container fluid class="h-100 pa-5 d-flex flex-column align-center">
      <v-card
        flat
        class="bg-shades-transparent d-flex flex-column align-center justify-center w-100"
      >
        <!-- 헤더 -->
        <v-card flat class="bg-shades-transparent w-100">
          <v-card-item class="text-h5 font-weight-bold pt-0 pa-5 pl-0">
            <div class="d-flex flex-row justify-start align-center">
              <div>Projects</div>
            </div>
          </v-card-item>
        </v-card>

        <!-- 검색/페이지 -->
        <v-card flat class="bg-shades-transparent w-100">
          <v-card flat class="bg-shades-transparent mb-4">
            <div class="d-flex justify-center flex-wrap align-center">
              <v-responsive
                max-width="180"
                min-width="180"
                class="mr-3 mt-3 mb-3"
              >
                <!-- ✅ [object Object] 해결 -->
                <v-select
                  v-model="data.params.searchType"
                  label="검색조건"
                  density="compact"
                  :items="searchOptions"
                  item-title="label"
                  item-value="value"
                  hide-details
                />
              </v-responsive>

              <v-responsive min-width="540" max-width="540">
                <v-text-field
                  v-model="data.params.searchText"
                  label="검색어"
                  density="compact"
                  clearable
                  required
                  class="mt-3 mb-3"
                  hide-details
                  @keyup.enter="doSearch"
                />
              </v-responsive>

              <div class="ml-3">
                <v-btn
                  size="large"
                  color="primary"
                  :rounded="5"
                  @click="doSearch"
                >
                  <v-icon>mdi-magnify</v-icon>
                </v-btn>
              </div>
            </div>
          </v-card>

          <!-- 상단 툴바 -->
          <v-sheet
            class="bg-shades-transparent d-flex flex-wrap align-center mb-2"
          >
            <v-sheet class="d-flex flex-wrap me-auto bg-shades-transparent">
              <v-sheet
                class="d-flex align-center mr-3 mb-2 bg-shades-transparent"
              >
                <v-chip color="primary"
                  >총 {{ data.totalDataLength.toLocaleString() }}개</v-chip
                >
              </v-sheet>

              <v-sheet class="bg-shades-transparent">
                <v-responsive max-width="140" min-width="140" class="mb-2">
                  <v-select
                    v-model="data.params.pageSize"
                    density="compact"
                    :items="pageSizeOptions"
                    item-title="text"
                    item-value="value"
                    variant="outlined"
                    color="primary"
                    hide-details
                    @update:model-value="changePageSize"
                  />
                </v-responsive>
              </v-sheet>
            </v-sheet>

            <v-sheet class="justify-end mb-2">
              <v-btn color="info" @click="openCreateModal"
                >Create Project</v-btn
              >
            </v-sheet>
          </v-sheet>

          <!-- 테이블 -->
          <v-card class="rounded-lg pa-8">
            <v-col cols="12">
              <v-sheet>
                <v-table
                  density="comfortable"
                  fixed-header
                  height="625"
                  overflow-x-auto
                >
                  <colgroup>
                    <col style="width: 5%" />
                    <col
                      v-for="(item, i) in tableHeader"
                      :key="i"
                      :style="`width:${item.width}`"
                    />
                  </colgroup>
                  <thead>
                    <tr>
                      <th>
                        <v-checkbox
                          v-model="data.allSelected"
                          style="min-width: 36px"
                          :indeterminate="data.allSelected === true"
                          hide-details
                          @change="getSelectedAllData"
                        />
                      </th>
                      <th
                        v-for="(item, i) in tableHeader"
                        :key="i"
                        class="text-center font-weight-bold"
                        :style="`${item.style}`"
                      >
                        {{ item.label }}
                      </th>
                    </tr>
                  </thead>

                  <tbody class="text-body-2">
                    <tr
                      v-for="(item, i) in data.results"
                      :key="i"
                      class="text-center"
                    >
                      <td>
                        <v-checkbox
                          v-model="data.selected"
                          hide-details
                          :value="{ deviceKey: item.deviceKey }"
                        />
                      </td>

                      <td>{{ item.no }}</td>
                      <td>{{ item.name }}</td>

                      <td>
                        <div class="truncate-2">{{ item.desc }}</div>
                      </td>

                      <td>
                        <template v-if="item.users?.length">
                          <v-chip
                            v-for="u in item.users"
                            :key="u"
                            size="small"
                            class="ma-1"
                            color="blue-lighten-2"
                            text-color="white"
                          >
                            {{ u }}
                          </v-chip>
                        </template>
                        <span v-else>-</span>
                      </td>

                      <td>{{ item.registDt }}</td>

                      <td style="white-space: nowrap">
                        <IconModifyBtn @on-click="openEditModal(item)" />
                        <IconDeleteBtn
                          @on-click="
                            deleteRows([{ deviceKey: item.deviceKey }])
                          "
                        />
                      </td>
                    </tr>
                  </tbody>
                </v-table>
              </v-sheet>

              <v-card-actions class="text-center mt-8 justify-center">
                <v-pagination
                  v-model="data.params.pageNum"
                  :length="data.pageLength"
                  :total-visible="10"
                  color="primary"
                  rounded="circle"
                  @update:model-value="changePageNum"
                />
              </v-card-actions>
            </v-col>
          </v-card>
        </v-card>
      </v-card>
    </v-container>

    <!-- 생성/수정 다이얼로그 -->
    <v-dialog v-model="data.isCreateVisible" max-width="560" persistent>
      <v-card>
        <v-card-title class="headline">
          {{
            data.modalMode === "create" ? "Create Project" : "Modify Project"
          }}
        </v-card-title>
        <v-card-text>
          <v-form>
            <v-text-field label="Project Name" v-model="form.prjNm" required />
            <v-textarea
              label="Description"
              v-model="form.prjDesc"
              rows="3"
              required
            />
            <v-select
              label="Select Users"
              v-model="form.selectedUsers"
              :items="userOptions"
              item-title="username"
              item-value="username"
              multiple
              chips
              closable-chips
            />
          </v-form>
        </v-card-text>
        <v-card-actions>
          <v-spacer />
          <v-btn color="primary" @click="saveProject">
            {{ data.modalMode === "create" ? "Create" : "Save" }}
          </v-btn>
          <v-btn text @click="data.isCreateVisible = false">Cancel</v-btn>
        </v-card-actions>
      </v-card>
    </v-dialog>
  </div>
</template>

<style scoped></style>
