<script setup lang="ts">
import { computed, onMounted, ref } from 'vue';
import DefaultLayout from '../layouts/DefaultLayout.vue';
import { trpc } from '../utils/trpc';

type AdminUserSanctions = {
    bannedUntil?: string;
    mutedUntil?: string;
    suspendedUntil?: string;
    warningCount?: number;
    flags?: string[];
    notes?: string;
    profileIconResetAt?: string;
    serverRestrictions?: Record<
        string,
        {
            blockedFeatures?: string[];
            until?: string;
            reason?: string;
            notes?: string;
        }
    >;
};

type AdminSanctionsPatch = {
    bannedUntil?: string | null;
    mutedUntil?: string | null;
    suspendedUntil?: string | null;
    warningCount?: number | null;
    flags?: string[] | null;
    notes?: string | null;
    profileIconResetAt?: string | null;
    serverRestrictions?: Record<
        string,
        {
            blockedFeatures?: string[];
            until?: string | null;
            reason?: string | null;
            notes?: string | null;
        } | null
    > | null;
};

type AdminUser = {
    id: string;
    username: string;
    displayName: string;
    roles: string[];
    sanctions: AdminUserSanctions;
    oauthType: string;
    oauthId?: string;
    email?: string;
    createdAt: string;
};

type AdminPublicUser = {
    id: string;
    username: string;
    displayName: string;
    roles: string[];
    createdAt: string;
};

type AdminProfile = {
    profileName: string;
    profile: string;
    scenario: string;
    status: string;
    apiPort: number;
    runtime: {
        apiRunning: boolean;
        daemonRunning: boolean;
    };
    buildCommitSha?: string;
    meta: Record<string, unknown>;
};

type ScenarioNationPreview = {
    id: number;
    name: string;
    color: string;
    cities: string[];
    generals: number;
    generalsEx: number;
    generalsNeutral: number;
};

type ScenarioPreview = {
    id: number;
    title: string;
    year: number | null;
    npcCount: number;
    npcExCount: number;
    npcNeutralCount: number;
    nations: ScenarioNationPreview[];
};

type ScenarioCatalogState = {
    scenarios: ScenarioPreview[];
    loading: boolean;
    status: string;
};

type InstallFormState = {
    scenarioId: number;
    turnTermMinutes: number;
    sync: boolean;
    fiction: number;
    extend: boolean;
    blockGeneralCreate: number;
    npcMode: number;
    showImgLevel: number;
    tournamentTrig: boolean;
    joinMode: 'full' | 'onlyRandom';
    autorunUserMinutes: number;
    autorunUserOptions: Record<string, boolean>;
    openAt: string;
    preopenAt: string;
    gitRef: string;
    reason: string;
};

type AdminAction =
    | 'RESUME'
    | 'PAUSE'
    | 'STOP'
    | 'ACCELERATE'
    | 'DELAY'
    | 'RESET_NOW'
    | 'RESET_SCHEDULED'
    | 'OPEN_SURVEY'
    | 'SHUTDOWN';

type AdminClient = {
    system: {
        getNotice: {
            query: () => Promise<{ notice: string }>;
        };
        setNotice: {
            mutate: (input: { notice: string }) => Promise<{ notice: string }>;
        };
    };
    users: {
        getLocalAccountStatus: {
            query: () => Promise<{ enabled: boolean }>;
        };
        createLocal: {
            mutate: (input: {
                username: string;
                password: string;
                displayName?: string;
            }) => Promise<{ user: AdminPublicUser }>;
        };
        lookup: {
            query: (input: { id?: string; username?: string; email?: string }) => Promise<AdminUser | null>;
        };
        resetPassword: {
            mutate: (input: { userId: string; newPassword?: string }) => Promise<{ password: string }>;
        };
        updateRoles: {
            mutate: (input: {
                userId: string;
                roles: string[];
                mode?: 'set' | 'grant' | 'revoke';
            }) => Promise<{ roles: string[] }>;
        };
        updateSanctions: {
            mutate: (input: {
                userId: string;
                patch: AdminSanctionsPatch;
            }) => Promise<{ sanctions: AdminUserSanctions }>;
        };
        setServerRestriction: {
            mutate: (input: {
                userId: string;
                profile: string;
                restriction: {
                    blockedFeatures?: string[];
                    until?: string | null;
                    reason?: string | null;
                    notes?: string | null;
                } | null;
            }) => Promise<{ sanctions: AdminUserSanctions }>;
        };
        resetProfileIcon: {
            mutate: (input: { userId: string }) => Promise<{ profileIconResetAt?: string }>;
        };
        forceDelete: {
            mutate: (input: { userId: string }) => Promise<{ ok: boolean }>;
        };
    };
    profiles: {
        list: {
            query: () => Promise<AdminProfile[]>;
        };
        listScenarios: {
            query: (input?: { gitRef?: string }) => Promise<ScenarioPreview[]>;
        };
        updateMeta: {
            mutate: (input: {
                profileName: string;
                patch: {
                    korName?: string | null;
                    color?: string | null;
                    inGameNotice?: string | null;
                    profileImageUrl?: string | null;
                    nextSeasonIdx?: number | null;
                };
            }) => Promise<AdminProfile | null>;
        };
        install: {
            mutate: (input: {
                profileName: string;
                install: {
                    scenarioId: number;
                    turnTermMinutes: number;
                    sync: boolean;
                    fiction: number;
                    extend: boolean;
                    blockGeneralCreate: number;
                    npcMode: number;
                    showImgLevel: number;
                    tournamentTrig: boolean;
                    joinMode: 'full' | 'onlyRandom';
                    autorunUser?: {
                        limitMinutes: number;
                        options: string[];
                    } | null;
                    openAt?: string;
                    preopenAt?: string;
                    gitRef?: string;
                };
                reason?: string;
            }) => Promise<{ ok: boolean; action?: unknown }>;
        };
        requestAction: {
            mutate: (input: {
                profileName: string;
                action: AdminAction;
                durationMinutes?: number;
                scheduledAt?: string;
                reason?: string;
            }) => Promise<{ ok: boolean }>;
        };
    };
};

const adminClient = trpc.admin as unknown as AdminClient;

const sessionToken = ref('');
const sessionTokenStatus = ref('');

if (typeof window !== 'undefined') {
    sessionToken.value = window.localStorage.getItem('sammo-session-token') ?? '';
}

const saveSessionToken = () => {
    const value = sessionToken.value.trim();
    if (typeof window !== 'undefined') {
        if (value) {
            window.localStorage.setItem('sammo-session-token', value);
        } else {
            window.localStorage.removeItem('sammo-session-token');
        }
    }
    sessionTokenStatus.value = value ? '저장됨' : '삭제됨';
};

const noticeDraft = ref('');
const noticeStatus = ref('');
const noticeLoading = ref(false);

const profiles = ref<AdminProfile[]>([]);
const profilesLoading = ref(false);
const profileEdits = ref<
    Record<
        string,
        {
            korName: string;
            color: string;
            inGameNotice: string;
            profileImageUrl: string;
            nextSeasonIdx: string;
        }
    >
>({});
const profileActions = ref<
    Record<
        string,
        {
            durationMinutes: string;
            scheduledAt: string;
            reason: string;
        }
    >
>({});
const profileActionStatus = ref<Record<string, string>>({});
const scenarioCatalogs = ref<Record<string, ScenarioCatalogState>>({});
const profileInstalls = ref<Record<string, InstallFormState>>({});
const profileInstallStatus = ref<Record<string, string>>({});

const autorunOptionLabels = [
    { key: 'develop', label: '내정' },
    { key: 'warp', label: '순간이동' },
    { key: 'recruit', label: '징병' },
    { key: 'recruit_high', label: '모병' },
    { key: 'train', label: '훈사' },
    { key: 'battle', label: '출병' },
    { key: 'chief', label: '기본 사령턴' },
] as const;

const turnTermOptions = [120, 60, 30, 20, 10, 5, 2, 1] as const;

const userLookupMode = ref<'username' | 'id' | 'email'>('username');
const userLookupValue = ref('');
const userLoading = ref(false);
const userError = ref('');
const userResult = ref<AdminUser | null>(null);

const localAccountEnabled = ref(false);
const localAccountStatus = ref('');
const localAccountResult = ref('');
const localAccountLoading = ref(false);
const localAccountForm = ref({
    username: '',
    password: '',
    displayName: '',
});

const passwordInput = ref('');
const passwordResult = ref('');
const passwordStatus = ref('');

const rolesInput = ref('');
const rolesMode = ref<'set' | 'grant' | 'revoke'>('grant');
const rolesStatus = ref('');

const banUntil = ref('');
const banReason = ref('');
const banStatus = ref('');

const profileIconStatus = ref('');

const restrictionProfile = ref('');
const restrictionFeatures = ref('');
const restrictionUntil = ref('');
const restrictionReason = ref('');
const restrictionNotes = ref('');
const restrictionStatus = ref('');

const forceDeleteStatus = ref('');

const hasUser = computed(() => Boolean(userResult.value));

const loadLocalAccountStatus = async () => {
    localAccountStatus.value = '';
    try {
        const result = await adminClient.users.getLocalAccountStatus.query();
        localAccountEnabled.value = result.enabled;
        localAccountStatus.value = result.enabled ? '' : 'ENV 설정이 비활성화 상태입니다.';
    } catch (error) {
        localAccountEnabled.value = false;
        localAccountStatus.value = '로컬 계정 생성 설정 확인 실패';
    }
};

const loadNotice = async () => {
    noticeLoading.value = true;
    try {
        const result = await adminClient.system.getNotice.query();
        noticeDraft.value = result.notice;
    } catch (error) {
        noticeStatus.value = '공지 불러오기 실패';
    } finally {
        noticeLoading.value = false;
    }
};

const saveNotice = async () => {
    noticeLoading.value = true;
    noticeStatus.value = '';
    try {
        const result = await adminClient.system.setNotice.mutate({
            notice: noticeDraft.value,
        });
        noticeDraft.value = result.notice;
        noticeStatus.value = '저장 완료';
    } catch (error) {
        noticeStatus.value = '저장 실패';
    } finally {
        noticeLoading.value = false;
    }
};

const ensureProfileBuffers = (profile: AdminProfile) => {
    if (!profileEdits.value[profile.profileName]) {
        const meta = (profile.meta ?? {}) as Record<string, unknown>;
        profileEdits.value[profile.profileName] = {
            korName: String(meta.korName ?? profile.profile),
            color: String(meta.color ?? '#ffffff'),
            inGameNotice: String(meta.inGameNotice ?? ''),
            profileImageUrl: String(meta.profileImageUrl ?? ''),
            nextSeasonIdx:
                typeof meta.nextSeasonIdx === 'number' && Number.isFinite(meta.nextSeasonIdx)
                    ? String(Math.floor(meta.nextSeasonIdx))
                    : '',
        };
    }
    if (!profileActions.value[profile.profileName]) {
        profileActions.value[profile.profileName] = {
            durationMinutes: '',
            scheduledAt: '',
            reason: '',
        };
    }
};

const pad2 = (value: number): string => String(value).padStart(2, '0');

const formatLocalInput = (date: Date): string =>
    `${date.getFullYear()}-${pad2(date.getMonth() + 1)}-${pad2(date.getDate())}T${pad2(date.getHours())}:${pad2(
        date.getMinutes()
    )}`;

const toLocalInputValue = (value: unknown): string => {
    if (typeof value !== 'string') {
        return '';
    }
    const parsed = new Date(value);
    if (Number.isNaN(parsed.getTime())) {
        return '';
    }
    return formatLocalInput(parsed);
};

const readNumber = (value: unknown, fallback: number): number =>
    typeof value === 'number' && Number.isFinite(value) ? value : fallback;

const readBoolean = (value: unknown, fallback: boolean): boolean =>
    typeof value === 'boolean' ? value : fallback;

const readString = (value: unknown, fallback: string): string => (typeof value === 'string' ? value : fallback);

const buildAutorunOptionMap = (options?: string[]): Record<string, boolean> => {
    const map: Record<string, boolean> = {};
    autorunOptionLabels.forEach(({ key }) => {
        map[key] = options ? options.includes(key) : true;
    });
    return map;
};

const normalizeGitRefInput = (value: string): string => value.trim();

const getScenarioCatalogKey = (gitRef: string): string => normalizeGitRefInput(gitRef);

const getScenarioCatalogStateByRef = (gitRef: string): ScenarioCatalogState => {
    const key = getScenarioCatalogKey(gitRef);
    return (
        scenarioCatalogs.value[key] ?? {
            scenarios: [],
            loading: false,
            status: '',
        }
    );
};

const ensureProfileInstallBuffers = (profile: AdminProfile) => {
    if (profileInstalls.value[profile.profileName]) {
        return;
    }
    const meta = (profile.meta ?? {}) as Record<string, unknown>;
    const install = (meta.install ?? {}) as Record<string, unknown>;
    const autorunUser = (install.autorunUser ?? {}) as Record<string, unknown>;
    const autorunOptionsRaw = Array.isArray(autorunUser.options)
        ? autorunUser.options.filter((option): option is string => typeof option === 'string')
        : undefined;
    const scenarioId = Number(profile.scenario);

    const installGitRef = readString(install.gitRef, '');
    const buildCommitRef = typeof profile.buildCommitSha === 'string' ? profile.buildCommitSha : '';

    profileInstalls.value[profile.profileName] = {
        scenarioId: Number.isFinite(scenarioId) ? scenarioId : readNumber(install.scenarioId, 0),
        turnTermMinutes: readNumber(install.turnTermMinutes, 60),
        sync: readBoolean(install.sync, true),
        fiction: readNumber(install.fiction, 1),
        extend: readBoolean(install.extend, true),
        blockGeneralCreate: readNumber(install.blockGeneralCreate, 0),
        npcMode: readNumber(install.npcMode, 0),
        showImgLevel: readNumber(install.showImgLevel, 3),
        tournamentTrig: readBoolean(install.tournamentTrig, true),
        joinMode: readString(install.joinMode, 'full') === 'onlyRandom' ? 'onlyRandom' : 'full',
        autorunUserMinutes: readNumber(autorunUser.limitMinutes, 1440),
        autorunUserOptions: buildAutorunOptionMap(autorunOptionsRaw),
        openAt: toLocalInputValue(install.openAt),
        preopenAt: toLocalInputValue(install.preopenAt),
        gitRef: installGitRef || buildCommitRef,
        reason: '',
    };
};

const buildScenarioMap = (items: ScenarioPreview[]): Map<number, ScenarioPreview> => {
    const map = new Map<number, ScenarioPreview>();
    items.forEach((scenario) => {
        map.set(scenario.id, scenario);
    });
    return map;
};

const buildScenarioGroups = (items: ScenarioPreview[]): Record<string, ScenarioPreview[]> => {
    const pattern = /【(.*?)[0-9\-_.a-zA-Z]*】/;
    const groups: Record<string, ScenarioPreview[]> = {};
    for (const scenario of items) {
        const match = pattern.exec(scenario.title);
        const category = match?.[1] ?? '기타';
        if (!groups[category]) {
            groups[category] = [];
        }
        groups[category].push(scenario);
    }
    return groups;
};

const getScenarioPreview = (profileName: string): ScenarioPreview | null => {
    const install = profileInstalls.value[profileName];
    if (!install) {
        return null;
    }
    const catalog = getScenarioCatalogStateByRef(install.gitRef);
    const map = buildScenarioMap(catalog.scenarios);
    return map.get(install.scenarioId) ?? null;
};

const getScenarioGroups = (profileName: string): Record<string, ScenarioPreview[]> => {
    const install = profileInstalls.value[profileName];
    if (!install) {
        return {};
    }
    const catalog = getScenarioCatalogStateByRef(install.gitRef);
    return buildScenarioGroups(catalog.scenarios);
};

const getScenarioLoading = (profileName: string): boolean => {
    const install = profileInstalls.value[profileName];
    if (!install) {
        return false;
    }
    return getScenarioCatalogStateByRef(install.gitRef).loading;
};

const getScenarioStatus = (profileName: string): string => {
    const install = profileInstalls.value[profileName];
    if (!install) {
        return '';
    }
    return getScenarioCatalogStateByRef(install.gitRef).status;
};

const loadProfiles = async () => {
    profilesLoading.value = true;
    try {
        const result = await adminClient.profiles.list.query();
        result.forEach((profile) => {
            ensureProfileBuffers(profile);
            ensureProfileInstallBuffers(profile);
        });
        profiles.value = result;
        const refs = new Set<string>();
        refs.add('');
        result.forEach((profile) => {
            const install = profileInstalls.value[profile.profileName];
            if (install?.gitRef) {
                refs.add(normalizeGitRefInput(install.gitRef));
            }
        });
        await Promise.all(Array.from(refs).map((gitRef) => loadScenarioCatalog(gitRef)));
    } catch (error) {
        profileActionStatus.value = {
            ...profileActionStatus.value,
            global: '프로필 목록을 불러오지 못했습니다.',
        };
    } finally {
        profilesLoading.value = false;
    }
};

const loadScenarioCatalog = async (gitRef: string) => {
    const key = getScenarioCatalogKey(gitRef);
    const previous = scenarioCatalogs.value[key];
    scenarioCatalogs.value = {
        ...scenarioCatalogs.value,
        [key]: {
            scenarios: previous?.scenarios ?? [],
            loading: true,
            status: '',
        },
    };
    try {
        const result = await adminClient.profiles.listScenarios.query(key ? { gitRef: key } : undefined);
        scenarioCatalogs.value = {
            ...scenarioCatalogs.value,
            [key]: {
                scenarios: result,
                loading: false,
                status: '',
            },
        };
    } catch (error) {
        scenarioCatalogs.value = {
            ...scenarioCatalogs.value,
            [key]: {
                scenarios: previous?.scenarios ?? [],
                loading: false,
                status: '시나리오 목록을 불러오지 못했습니다.',
            },
        };
    }
};

const loadScenariosForProfile = async (profileName: string) => {
    const install = profileInstalls.value[profileName];
    if (!install) {
        return;
    }
    await loadScenarioCatalog(install.gitRef);
};

const updateProfileMeta = async (profileName: string) => {
    const edit = profileEdits.value[profileName];
    if (!edit) {
        return;
    }
    const nextSeasonRaw = edit.nextSeasonIdx.trim();
    const nextSeasonIdx = nextSeasonRaw === '' ? null : Number(nextSeasonRaw);
    if (nextSeasonIdx !== null && (!Number.isFinite(nextSeasonIdx) || nextSeasonIdx < 0)) {
        profileActionStatus.value = {
            ...profileActionStatus.value,
            [profileName]: '다음 시즌 번호는 0 이상 숫자여야 합니다.',
        };
        return;
    }
    const patch = {
        korName: edit.korName.trim() || null,
        color: edit.color.trim() || null,
        inGameNotice: edit.inGameNotice.trim() || null,
        profileImageUrl: edit.profileImageUrl.trim() || null,
        nextSeasonIdx: nextSeasonIdx === null ? null : Math.floor(nextSeasonIdx),
    };
    try {
        const updated = await adminClient.profiles.updateMeta.mutate({
            profileName,
            patch,
        });
        profileActionStatus.value = {
            ...profileActionStatus.value,
            [profileName]: updated ? '메타 저장 완료' : '메타 저장 실패',
        };
        if (updated) {
            profiles.value = profiles.value.map((item) =>
                item.profileName === profileName ? { ...item, ...updated } : item
            );
        }
    } catch (error) {
        profileActionStatus.value = {
            ...profileActionStatus.value,
            [profileName]: '메타 저장 실패',
        };
    }
};

const requestProfileAction = async (profileName: string, action: AdminAction) => {
    const actionState = profileActions.value[profileName];
    const durationMinutes = actionState?.durationMinutes ? Number(actionState.durationMinutes) : undefined;
    const durationValue = durationMinutes && durationMinutes > 0 ? durationMinutes : undefined;
    const scheduledAt = actionState?.scheduledAt ? new Date(actionState.scheduledAt).toISOString() : undefined;
    const reason = actionState?.reason.trim() || undefined;
    try {
        await adminClient.profiles.requestAction.mutate({
            profileName,
            action,
            durationMinutes: durationValue,
            scheduledAt,
            reason,
        });
        profileActionStatus.value = {
            ...profileActionStatus.value,
            [profileName]: `요청 완료: ${action}`,
        };
    } catch (error) {
        profileActionStatus.value = {
            ...profileActionStatus.value,
            [profileName]: `요청 실패: ${action}`,
        };
    }
};

const requestInstall = async (profileName: string) => {
    const install = profileInstalls.value[profileName];
    if (!install) {
        return;
    }
    const options = Object.entries(install.autorunUserOptions)
        .filter(([, enabled]) => enabled)
        .map(([key]) => key);
    const autorunUser =
        install.autorunUserMinutes > 0 && options.length
            ? {
                  limitMinutes: install.autorunUserMinutes,
                  options,
              }
            : null;
    const openAt = install.openAt ? new Date(install.openAt) : null;
    if (openAt && Number.isNaN(openAt.getTime())) {
        profileInstallStatus.value = {
            ...profileInstallStatus.value,
            [profileName]: '오픈 시간이 올바르지 않습니다.',
        };
        return;
    }
    const preopenAt = install.preopenAt ? new Date(install.preopenAt) : null;
    if (preopenAt && Number.isNaN(preopenAt.getTime())) {
        profileInstallStatus.value = {
            ...profileInstallStatus.value,
            [profileName]: '가오픈 시간이 올바르지 않습니다.',
        };
        return;
    }
    try {
        const gitRef = normalizeGitRefInput(install.gitRef);
        await adminClient.profiles.install.mutate({
            profileName,
            install: {
                scenarioId: install.scenarioId,
                turnTermMinutes: install.turnTermMinutes,
                sync: install.sync,
                fiction: install.fiction,
                extend: install.extend,
                blockGeneralCreate: install.blockGeneralCreate,
                npcMode: install.npcMode,
                showImgLevel: install.showImgLevel,
                tournamentTrig: install.tournamentTrig,
                joinMode: install.joinMode,
                autorunUser,
                openAt: openAt ? openAt.toISOString() : undefined,
                preopenAt: preopenAt ? preopenAt.toISOString() : undefined,
                gitRef: gitRef ? gitRef : undefined,
            },
            reason: install.reason.trim() || undefined,
        });
        profileInstallStatus.value = {
            ...profileInstallStatus.value,
            [profileName]: openAt ? '설치 예약 완료' : '설치 요청 완료',
        };
        await loadProfiles();
    } catch (error) {
        profileInstallStatus.value = {
            ...profileInstallStatus.value,
            [profileName]: '설치 요청 실패',
        };
    }
};

const lookupUser = async () => {
    userLoading.value = true;
    userError.value = '';
    passwordStatus.value = '';
    rolesStatus.value = '';
    banStatus.value = '';
    restrictionStatus.value = '';
    profileIconStatus.value = '';
    forceDeleteStatus.value = '';
    try {
        const payload =
            userLookupMode.value === 'id'
                ? { id: userLookupValue.value.trim() }
                : userLookupMode.value === 'email'
                  ? { email: userLookupValue.value.trim() }
                  : { username: userLookupValue.value.trim() };
        const result = await adminClient.users.lookup.query(payload);
        if (!result) {
            userResult.value = null;
            userError.value = '사용자를 찾을 수 없습니다.';
            return;
        }
        userResult.value = result;
    } catch (error) {
        userError.value = '조회 실패';
    } finally {
        userLoading.value = false;
    }
};

const resetUserPassword = async () => {
    if (!userResult.value) {
        return;
    }
    passwordStatus.value = '';
    passwordResult.value = '';
    try {
        const result = await adminClient.users.resetPassword.mutate({
            userId: userResult.value.id,
            newPassword: passwordInput.value.trim() || undefined,
        });
        passwordResult.value = result.password;
        passwordStatus.value = '초기화 완료';
        passwordInput.value = '';
    } catch (error) {
        passwordStatus.value = '초기화 실패';
    }
};

const updateUserRoles = async () => {
    if (!userResult.value) {
        return;
    }
    const roles = rolesInput.value
        .split(',')
        .map((role) => role.trim())
        .filter(Boolean);
    if (!roles.length) {
        rolesStatus.value = '역할을 입력하세요.';
        return;
    }
    rolesStatus.value = '';
    try {
        const result = await adminClient.users.updateRoles.mutate({
            userId: userResult.value.id,
            roles,
            mode: rolesMode.value,
        });
        userResult.value = { ...userResult.value, roles: result.roles };
        rolesStatus.value = '권한 업데이트 완료';
    } catch (error) {
        rolesStatus.value = '권한 업데이트 실패';
    }
};

const applyBan = async () => {
    if (!userResult.value) {
        return;
    }
    const until = banUntil.value ? new Date(banUntil.value).toISOString() : null;
    const patch = {
        bannedUntil: until,
        notes: banReason.value.trim() || undefined,
    };
    try {
        const result = await adminClient.users.updateSanctions.mutate({
            userId: userResult.value.id,
            patch,
        });
        userResult.value = { ...userResult.value, sanctions: result.sanctions };
        banStatus.value = '차단 설정 완료';
    } catch (error) {
        banStatus.value = '차단 설정 실패';
    }
};

const clearBan = async () => {
    if (!userResult.value) {
        return;
    }
    try {
        const result = await adminClient.users.updateSanctions.mutate({
            userId: userResult.value.id,
            patch: { bannedUntil: null },
        });
        userResult.value = { ...userResult.value, sanctions: result.sanctions };
        banStatus.value = '차단 해제 완료';
    } catch (error) {
        banStatus.value = '차단 해제 실패';
    }
};

const resetProfileIcon = async () => {
    if (!userResult.value) {
        return;
    }
    try {
        const result = await adminClient.users.resetProfileIcon.mutate({
            userId: userResult.value.id,
        });
        userResult.value = {
            ...userResult.value,
            sanctions: {
                ...userResult.value.sanctions,
                profileIconResetAt: result.profileIconResetAt,
            },
        };
        profileIconStatus.value = '아이콘 초기화 요청 완료';
    } catch (error) {
        profileIconStatus.value = '아이콘 초기화 실패';
    }
};

const applyRestriction = async () => {
    if (!userResult.value) {
        return;
    }
    if (!restrictionProfile.value.trim()) {
        restrictionStatus.value = '서버 프로필명을 입력하세요.';
        return;
    }
    const features = restrictionFeatures.value
        .split(',')
        .map((feature) => feature.trim())
        .filter(Boolean);
    const restriction = {
        blockedFeatures: features.length ? features : undefined,
        until: restrictionUntil.value ? new Date(restrictionUntil.value).toISOString() : undefined,
        reason: restrictionReason.value.trim() || undefined,
        notes: restrictionNotes.value.trim() || undefined,
    };
    try {
        const result = await adminClient.users.setServerRestriction.mutate({
            userId: userResult.value.id,
            profile: restrictionProfile.value.trim(),
            restriction,
        });
        userResult.value = { ...userResult.value, sanctions: result.sanctions };
        restrictionStatus.value = '서버 제재 적용 완료';
    } catch (error) {
        restrictionStatus.value = '서버 제재 적용 실패';
    }
};

const clearRestriction = async () => {
    if (!userResult.value) {
        return;
    }
    if (!restrictionProfile.value.trim()) {
        restrictionStatus.value = '서버 프로필명을 입력하세요.';
        return;
    }
    try {
        const result = await adminClient.users.setServerRestriction.mutate({
            userId: userResult.value.id,
            profile: restrictionProfile.value.trim(),
            restriction: null,
        });
        userResult.value = { ...userResult.value, sanctions: result.sanctions };
        restrictionStatus.value = '서버 제재 해제 완료';
    } catch (error) {
        restrictionStatus.value = '서버 제재 해제 실패';
    }
};

const forceDeleteUser = async () => {
    if (!userResult.value) {
        return;
    }
    if (typeof window !== 'undefined') {
        const confirmed = window.confirm('정말로 강제 탈퇴 처리하시겠습니까?');
        if (!confirmed) {
            return;
        }
    }
    try {
        await adminClient.users.forceDelete.mutate({ userId: userResult.value.id });
        userResult.value = null;
        forceDeleteStatus.value = '강제 탈퇴 완료';
    } catch (error) {
        forceDeleteStatus.value = '강제 탈퇴 실패';
    }
};

const createLocalAccount = async () => {
    localAccountStatus.value = '';
    localAccountResult.value = '';
    if (!localAccountEnabled.value) {
        localAccountStatus.value = 'ENV 설정이 비활성화 상태입니다.';
        return;
    }
    const username = localAccountForm.value.username.trim();
    const password = localAccountForm.value.password.trim();
    const displayName = localAccountForm.value.displayName.trim();
    if (!username || !password) {
        localAccountStatus.value = '아이디와 비밀번호를 입력하세요.';
        return;
    }
    localAccountLoading.value = true;
    try {
        const result = await adminClient.users.createLocal.mutate({
            username,
            password,
            displayName: displayName || undefined,
        });
        localAccountResult.value = `생성됨: ${result.user.username} (${result.user.id})`;
        localAccountStatus.value = '로컬 계정 생성 완료';
        localAccountForm.value = {
            username: result.user.username,
            password: '',
            displayName: '',
        };
        userLookupMode.value = 'username';
        userLookupValue.value = result.user.username;
        await lookupUser();
    } catch (error) {
        localAccountStatus.value = '로컬 계정 생성 실패';
    } finally {
        localAccountLoading.value = false;
    }
};

onMounted(() => {
    void loadLocalAccountStatus();
    void loadNotice();
    void loadProfiles();
});
</script>

<template>
    <DefaultLayout>
        <div class="max-w-6xl mx-auto px-4 py-10 space-y-10">
            <div class="flex flex-col gap-4 md:flex-row md:items-end md:justify-between">
                <div class="space-y-2">
                    <h2 class="text-3xl font-bold text-white">관리자 콘솔</h2>
                    <p class="text-sm text-zinc-400">유저 관리와 서버 운영 제어를 위한 관리자 전용 대시보드입니다.</p>
                </div>
                <RouterLink
                    to="/admin/server-operations"
                    class="rounded bg-amber-500 px-4 py-2 text-center text-sm font-bold text-black hover:bg-amber-400"
                >
                    서버 배포 · 시나리오 초기화
                </RouterLink>
            </div>

            <section class="bg-zinc-900 border border-zinc-800 rounded-lg p-5 space-y-3">
                <div class="flex items-center justify-between">
                    <h3 class="text-lg font-semibold">관리자 세션 토큰</h3>
                    <span class="text-xs text-zinc-500">{{ sessionTokenStatus }}</span>
                </div>
                <div class="flex flex-col md:flex-row gap-3">
                    <input
                        v-model="sessionToken"
                        type="password"
                        class="flex-1 bg-zinc-950 border border-zinc-700 rounded px-3 py-2 text-sm text-white focus:outline-none focus:border-yellow-500"
                        placeholder="세션 토큰 입력"
                    />
                    <button
                        class="bg-yellow-600 hover:bg-yellow-500 text-black font-semibold px-4 py-2 rounded"
                        @click="saveSessionToken"
                    >
                        저장
                    </button>
                </div>
            </section>

            <div class="grid lg:grid-cols-2 gap-8">
                <section class="space-y-6">
                    <div class="bg-zinc-900 border border-zinc-800 rounded-lg p-5 space-y-4">
                        <h3 class="text-lg font-semibold">유저 관리</h3>
                        <form class="space-y-3" @submit.prevent="lookupUser">
                            <div class="flex flex-col md:flex-row gap-2">
                                <select
                                    v-model="userLookupMode"
                                    class="bg-zinc-950 border border-zinc-700 rounded px-3 py-2 text-sm text-white"
                                >
                                    <option value="username">계정명</option>
                                    <option value="email">이메일</option>
                                    <option value="id">UUID</option>
                                </select>
                                <input
                                    v-model="userLookupValue"
                                    type="text"
                                    class="flex-1 bg-zinc-950 border border-zinc-700 rounded px-3 py-2 text-sm text-white focus:outline-none focus:border-yellow-500"
                                    placeholder="검색 값 입력"
                                />
                                <button
                                    class="bg-blue-700 hover:bg-blue-600 text-white font-semibold px-4 py-2 rounded"
                                    :disabled="userLoading"
                                >
                                    조회
                                </button>
                            </div>
                            <div v-if="userError" class="text-xs text-red-400">{{ userError }}</div>
                        </form>

                        <div v-if="userResult" class="bg-zinc-950 border border-zinc-800 rounded p-4 space-y-2">
                            <div class="flex justify-between items-center">
                                <div class="text-sm font-semibold">{{ userResult.username }}</div>
                                <div class="text-xs text-zinc-500">{{ userResult.id }}</div>
                            </div>
                            <div class="text-xs text-zinc-400">표시명: {{ userResult.displayName }}</div>
                            <div class="text-xs text-zinc-400">권한: {{ userResult.roles.join(', ') || '-' }}</div>
                            <div class="text-xs text-zinc-500">
                                OAuth: {{ userResult.oauthType }} {{ userResult.email ?? '' }}
                            </div>
                            <div class="text-xs text-zinc-500">가입일: {{ userResult.createdAt }}</div>
                            <div class="text-xs text-zinc-400 mt-2">제재 상태</div>
                            <pre class="text-[11px] text-zinc-400 bg-black/50 p-2 rounded whitespace-pre-wrap"
                                >{{ JSON.stringify(userResult.sanctions, null, 2) }}
              </pre
                            >
                        </div>
                    </div>

                    <div class="bg-zinc-900 border border-zinc-800 rounded-lg p-5 space-y-4">
                        <div class="flex items-center justify-between">
                            <h4 class="text-base font-semibold">로컬 계정 생성</h4>
                            <span class="text-xs text-zinc-500">
                                ENV {{ localAccountEnabled ? 'ON' : 'OFF' }}
                            </span>
                        </div>
                        <div class="text-xs text-zinc-500">
                            카카오 OAuth 없이 로그인 가능한 계정을 생성합니다.
                        </div>
                        <div class="grid gap-2">
                            <input
                                v-model="localAccountForm.username"
                                type="text"
                                class="bg-zinc-950 border border-zinc-700 rounded px-3 py-2 text-sm text-white"
                                placeholder="아이디"
                                :disabled="!localAccountEnabled || localAccountLoading"
                            />
                            <input
                                v-model="localAccountForm.password"
                                type="password"
                                class="bg-zinc-950 border border-zinc-700 rounded px-3 py-2 text-sm text-white"
                                placeholder="비밀번호"
                                :disabled="!localAccountEnabled || localAccountLoading"
                            />
                            <input
                                v-model="localAccountForm.displayName"
                                type="text"
                                class="bg-zinc-950 border border-zinc-700 rounded px-3 py-2 text-sm text-white"
                                placeholder="표시명 (선택)"
                                :disabled="!localAccountEnabled || localAccountLoading"
                            />
                            <button
                                class="bg-cyan-600 hover:bg-cyan-500 text-black font-semibold px-4 py-2 rounded"
                                :disabled="!localAccountEnabled || localAccountLoading"
                                @click="createLocalAccount"
                            >
                                계정 생성
                            </button>
                        </div>
                        <div class="text-xs text-zinc-500">{{ localAccountStatus }}</div>
                        <div v-if="localAccountResult" class="text-xs text-emerald-400">
                            {{ localAccountResult }}
                        </div>
                    </div>

                    <div class="bg-zinc-900 border border-zinc-800 rounded-lg p-5 space-y-4">
                        <h4 class="text-base font-semibold">비밀번호 리셋</h4>
                        <div class="flex flex-col md:flex-row gap-2">
                            <input
                                v-model="passwordInput"
                                type="text"
                                class="flex-1 bg-zinc-950 border border-zinc-700 rounded px-3 py-2 text-sm text-white"
                                placeholder="직접 지정 시 입력"
                                :disabled="!hasUser"
                            />
                            <button
                                class="bg-emerald-600 hover:bg-emerald-500 text-black font-semibold px-4 py-2 rounded"
                                :disabled="!hasUser"
                                @click="resetUserPassword"
                            >
                                초기화
                            </button>
                        </div>
                        <div class="text-xs text-zinc-400">임시 비밀번호: {{ passwordResult || '-' }}</div>
                        <div class="text-xs text-zinc-500">{{ passwordStatus }}</div>
                    </div>

                    <div class="bg-zinc-900 border border-zinc-800 rounded-lg p-5 space-y-4">
                        <h4 class="text-base font-semibold">특수 권한 부여</h4>
                        <div class="flex flex-col md:flex-row gap-2">
                            <select
                                v-model="rolesMode"
                                class="bg-zinc-950 border border-zinc-700 rounded px-3 py-2 text-sm text-white"
                                :disabled="!hasUser"
                            >
                                <option value="grant">추가</option>
                                <option value="revoke">제거</option>
                                <option value="set">덮어쓰기</option>
                            </select>
                            <input
                                v-model="rolesInput"
                                type="text"
                                class="flex-1 bg-zinc-950 border border-zinc-700 rounded px-3 py-2 text-sm text-white"
                                placeholder="admin, moderator 등"
                                :disabled="!hasUser"
                            />
                            <button
                                class="bg-indigo-600 hover:bg-indigo-500 text-white font-semibold px-4 py-2 rounded"
                                :disabled="!hasUser"
                                @click="updateUserRoles"
                            >
                                적용
                            </button>
                        </div>
                        <div class="text-xs text-zinc-500">{{ rolesStatus }}</div>
                    </div>

                    <div class="bg-zinc-900 border border-zinc-800 rounded-lg p-5 space-y-4">
                        <h4 class="text-base font-semibold">유저 차단</h4>
                        <div class="flex flex-col gap-2">
                            <input
                                v-model="banUntil"
                                type="datetime-local"
                                class="bg-zinc-950 border border-zinc-700 rounded px-3 py-2 text-sm text-white"
                                :disabled="!hasUser"
                            />
                            <input
                                v-model="banReason"
                                type="text"
                                class="bg-zinc-950 border border-zinc-700 rounded px-3 py-2 text-sm text-white"
                                placeholder="사유"
                                :disabled="!hasUser"
                            />
                            <div class="flex gap-2">
                                <button
                                    class="bg-red-600 hover:bg-red-500 text-white font-semibold px-4 py-2 rounded"
                                    :disabled="!hasUser"
                                    @click="applyBan"
                                >
                                    차단 설정
                                </button>
                                <button
                                    class="bg-zinc-700 hover:bg-zinc-600 text-white font-semibold px-4 py-2 rounded"
                                    :disabled="!hasUser"
                                    @click="clearBan"
                                >
                                    차단 해제
                                </button>
                            </div>
                        </div>
                        <div class="text-xs text-zinc-500">{{ banStatus }}</div>
                    </div>

                    <div class="bg-zinc-900 border border-zinc-800 rounded-lg p-5 space-y-4">
                        <h4 class="text-base font-semibold">서버별 기능 제재</h4>
                        <div class="grid gap-2">
                            <input
                                v-model="restrictionProfile"
                                type="text"
                                class="bg-zinc-950 border border-zinc-700 rounded px-3 py-2 text-sm text-white"
                                placeholder="profile:scenario"
                                :disabled="!hasUser"
                            />
                            <input
                                v-model="restrictionFeatures"
                                type="text"
                                class="bg-zinc-950 border border-zinc-700 rounded px-3 py-2 text-sm text-white"
                                placeholder="login, message 등"
                                :disabled="!hasUser"
                            />
                            <input
                                v-model="restrictionUntil"
                                type="datetime-local"
                                class="bg-zinc-950 border border-zinc-700 rounded px-3 py-2 text-sm text-white"
                                :disabled="!hasUser"
                            />
                            <input
                                v-model="restrictionReason"
                                type="text"
                                class="bg-zinc-950 border border-zinc-700 rounded px-3 py-2 text-sm text-white"
                                placeholder="사유"
                                :disabled="!hasUser"
                            />
                            <input
                                v-model="restrictionNotes"
                                type="text"
                                class="bg-zinc-950 border border-zinc-700 rounded px-3 py-2 text-sm text-white"
                                placeholder="메모"
                                :disabled="!hasUser"
                            />
                            <div class="flex gap-2">
                                <button
                                    class="bg-amber-600 hover:bg-amber-500 text-black font-semibold px-4 py-2 rounded"
                                    :disabled="!hasUser"
                                    @click="applyRestriction"
                                >
                                    제재 적용
                                </button>
                                <button
                                    class="bg-zinc-700 hover:bg-zinc-600 text-white font-semibold px-4 py-2 rounded"
                                    :disabled="!hasUser"
                                    @click="clearRestriction"
                                >
                                    제재 해제
                                </button>
                            </div>
                        </div>
                        <div class="text-xs text-zinc-500">{{ restrictionStatus }}</div>
                    </div>

                    <div class="bg-zinc-900 border border-zinc-800 rounded-lg p-5 space-y-4">
                        <h4 class="text-base font-semibold">프로필 아이콘 초기화</h4>
                        <button
                            class="bg-purple-600 hover:bg-purple-500 text-white font-semibold px-4 py-2 rounded"
                            :disabled="!hasUser"
                            @click="resetProfileIcon"
                        >
                            아이콘 초기화 요청
                        </button>
                        <div class="text-xs text-zinc-500">{{ profileIconStatus }}</div>
                    </div>

                    <div class="bg-zinc-900 border border-zinc-800 rounded-lg p-5 space-y-4">
                        <h4 class="text-base font-semibold text-red-400">강제 탈퇴</h4>
                        <button
                            class="bg-red-700 hover:bg-red-600 text-white font-semibold px-4 py-2 rounded"
                            :disabled="!hasUser"
                            @click="forceDeleteUser"
                        >
                            강제 탈퇴 처리
                        </button>
                        <div class="text-xs text-zinc-500">{{ forceDeleteStatus }}</div>
                    </div>
                </section>

                <section class="space-y-6">
                    <div class="bg-zinc-900 border border-zinc-800 rounded-lg p-5 space-y-4">
                        <div class="flex justify-between items-center">
                            <h3 class="text-lg font-semibold">서버 공지</h3>
                            <span class="text-xs text-zinc-500">{{ noticeStatus }}</span>
                        </div>
                        <textarea
                            v-model="noticeDraft"
                            class="w-full bg-zinc-950 border border-zinc-700 rounded p-3 text-sm text-white min-h-[140px]"
                            placeholder="로비 공지 입력"
                        />
                        <div class="flex gap-2">
                            <button
                                class="bg-blue-700 hover:bg-blue-600 text-white font-semibold px-4 py-2 rounded"
                                :disabled="noticeLoading"
                                @click="saveNotice"
                            >
                                공지 저장
                            </button>
                            <button
                                class="bg-zinc-700 hover:bg-zinc-600 text-white font-semibold px-4 py-2 rounded"
                                :disabled="noticeLoading"
                                @click="loadNotice"
                            >
                                다시 불러오기
                            </button>
                        </div>
                    </div>

                    <div class="space-y-4">
                        <div class="flex items-center justify-between">
                            <h3 class="text-lg font-semibold">서버별 관리</h3>
                            <button
                                class="bg-zinc-700 hover:bg-zinc-600 text-white text-sm px-3 py-1.5 rounded"
                                :disabled="profilesLoading"
                                @click="loadProfiles"
                            >
                                새로고침
                            </button>
                        </div>
                        <div
                            v-for="profile in profiles"
                            :key="profile.profileName"
                            class="bg-zinc-900 border border-zinc-800 rounded-lg p-5 space-y-4"
                        >
                            <div class="flex flex-col md:flex-row md:items-center md:justify-between gap-2">
                                <div>
                                    <div class="text-base font-semibold">
                                        {{ profile.profileName }} ({{ profile.profile }})
                                    </div>
                                    <div class="text-xs text-zinc-500">시나리오: {{ profile.scenario }}</div>
                                </div>
                                <div class="text-xs text-zinc-400">
                                    상태: {{ profile.status }} / API: {{ profile.runtime.apiRunning ? 'ON' : 'OFF' }} /
                                    DAEMON: {{ profile.runtime.daemonRunning ? 'ON' : 'OFF' }}
                                </div>
                            </div>

                            <div class="text-xs text-zinc-400">빌드 커밋: {{ profile.buildCommitSha ?? '미지정' }}</div>

                            <div class="grid md:grid-cols-2 gap-3">
                                <div class="space-y-2">
                                    <label class="text-xs text-zinc-400">표시명</label>
                                    <input
                                        v-model="profileEdits[profile.profileName].korName"
                                        type="text"
                                        class="w-full bg-zinc-950 border border-zinc-700 rounded px-3 py-2 text-sm text-white"
                                    />
                                    <label class="text-xs text-zinc-400">색상</label>
                                    <input
                                        v-model="profileEdits[profile.profileName].color"
                                        type="text"
                                        class="w-full bg-zinc-950 border border-zinc-700 rounded px-3 py-2 text-sm text-white"
                                    />
                                    <label class="text-xs text-zinc-400">인게임 공지</label>
                                    <textarea
                                        v-model="profileEdits[profile.profileName].inGameNotice"
                                        class="w-full bg-zinc-950 border border-zinc-700 rounded p-2 text-sm text-white min-h-[80px]"
                                    />
                                    <label class="text-xs text-zinc-400">프로필 이미지</label>
                                    <input
                                        v-model="profileEdits[profile.profileName].profileImageUrl"
                                        type="text"
                                        class="w-full bg-zinc-950 border border-zinc-700 rounded px-3 py-2 text-sm text-white"
                                    />
                                    <label class="text-xs text-zinc-400">다음 시즌 번호</label>
                                    <input
                                        v-model="profileEdits[profile.profileName].nextSeasonIdx"
                                        type="number"
                                        min="0"
                                        step="1"
                                        class="w-full bg-zinc-950 border border-zinc-700 rounded px-3 py-2 text-sm text-white"
                                        placeholder="예: 12"
                                    />
                                    <div class="text-xs text-zinc-500">리셋 시 적용할 시즌 번호를 지정합니다.</div>
                                    <button
                                        class="bg-emerald-600 hover:bg-emerald-500 text-black font-semibold px-4 py-2 rounded"
                                        @click="updateProfileMeta(profile.profileName)"
                                    >
                                        메타 저장
                                    </button>
                                </div>

                                <div class="space-y-2">
                                    <label class="text-xs text-zinc-400">특수 동작 메모</label>
                                    <input
                                        v-model="profileActions[profile.profileName].reason"
                                        type="text"
                                        class="w-full bg-zinc-950 border border-zinc-700 rounded px-3 py-2 text-sm text-white"
                                        placeholder="사유 / 메모"
                                    />
                                    <label class="text-xs text-zinc-400">가속/연기 (분)</label>
                                    <input
                                        v-model="profileActions[profile.profileName].durationMinutes"
                                        type="number"
                                        min="1"
                                        max="1440"
                                        class="w-full bg-zinc-950 border border-zinc-700 rounded px-3 py-2 text-sm text-white"
                                    />
                                    <label class="text-xs text-zinc-400">리셋 예약</label>
                                    <input
                                        v-model="profileActions[profile.profileName].scheduledAt"
                                        type="datetime-local"
                                        class="w-full bg-zinc-950 border border-zinc-700 rounded px-3 py-2 text-sm text-white"
                                    />
                                    <div class="grid grid-cols-2 gap-2 pt-2">
                                        <button
                                            class="bg-blue-700 hover:bg-blue-600 text-white font-semibold px-3 py-2 rounded"
                                            @click="requestProfileAction(profile.profileName, 'RESUME')"
                                        >
                                            재개
                                        </button>
                                        <button
                                            class="bg-zinc-700 hover:bg-zinc-600 text-white font-semibold px-3 py-2 rounded"
                                            @click="requestProfileAction(profile.profileName, 'PAUSE')"
                                        >
                                            일시정지
                                        </button>
                                        <button
                                            class="bg-red-700 hover:bg-red-600 text-white font-semibold px-3 py-2 rounded"
                                            @click="requestProfileAction(profile.profileName, 'STOP')"
                                        >
                                            중지
                                        </button>
                                        <button
                                            class="bg-orange-600 hover:bg-orange-500 text-black font-semibold px-3 py-2 rounded"
                                            @click="requestProfileAction(profile.profileName, 'ACCELERATE')"
                                        >
                                            가속
                                        </button>
                                        <button
                                            class="bg-yellow-600 hover:bg-yellow-500 text-black font-semibold px-3 py-2 rounded"
                                            @click="requestProfileAction(profile.profileName, 'DELAY')"
                                        >
                                            연기
                                        </button>
                                        <button
                                            class="bg-purple-600 hover:bg-purple-500 text-white font-semibold px-3 py-2 rounded"
                                            @click="requestProfileAction(profile.profileName, 'RESET_NOW')"
                                        >
                                            즉시 리셋
                                        </button>
                                        <button
                                            class="bg-purple-800 hover:bg-purple-700 text-white font-semibold px-3 py-2 rounded"
                                            @click="requestProfileAction(profile.profileName, 'RESET_SCHEDULED')"
                                        >
                                            리셋 예약
                                        </button>
                                        <button
                                            class="bg-teal-700 hover:bg-teal-600 text-white font-semibold px-3 py-2 rounded"
                                            @click="requestProfileAction(profile.profileName, 'OPEN_SURVEY')"
                                        >
                                            설문 오픈
                                        </button>
                                        <button
                                            class="bg-black hover:bg-zinc-800 text-white font-semibold px-3 py-2 rounded col-span-2"
                                            @click="requestProfileAction(profile.profileName, 'SHUTDOWN')"
                                        >
                                            서버 폐쇄
                                        </button>
                                    </div>
                                    <div class="text-xs text-zinc-500">
                                        {{ profileActionStatus[profile.profileName] }}
                                    </div>
                                </div>
                            </div>

                            <div
                                v-if="profileInstalls[profile.profileName]"
                                class="border-t border-zinc-800 pt-4 space-y-3"
                            >
                                <div class="flex items-center justify-between">
                                    <h4 class="text-sm font-semibold">설치/리셋</h4>
                                    <span class="text-xs text-zinc-500">{{ profileInstallStatus[profile.profileName] }}</span>
                                </div>
                                <div class="grid lg:grid-cols-2 gap-4">
                                    <div class="space-y-3">
                                        <div class="space-y-1">
                                            <label class="text-xs text-zinc-400">Git ref (선택)</label>
                                            <div class="flex gap-2">
                                                <input
                                                    v-model="profileInstalls[profile.profileName].gitRef"
                                                    type="text"
                                                    class="flex-1 bg-zinc-950 border border-zinc-700 rounded px-3 py-2 text-sm text-white"
                                                    placeholder="main / v1.0.0 / abc123"
                                                />
                                                <button
                                                    class="bg-zinc-700 hover:bg-zinc-600 text-white text-sm px-3 py-2 rounded"
                                                    :disabled="getScenarioLoading(profile.profileName)"
                                                    @click="loadScenariosForProfile(profile.profileName)"
                                                >
                                                    불러오기
                                                </button>
                                            </div>
                                            <div class="text-xs text-zinc-500">비워두면 현재 저장소 기준으로 불러옵니다.</div>
                                        </div>

                                        <div class="space-y-1">
                                            <label class="text-xs text-zinc-400">시나리오 선택</label>
                                            <select
                                                v-model.number="profileInstalls[profile.profileName].scenarioId"
                                                class="w-full bg-zinc-950 border border-zinc-700 rounded px-3 py-2 text-sm text-white"
                                                :disabled="getScenarioLoading(profile.profileName)"
                                            >
                                                <option v-if="getScenarioLoading(profile.profileName)" disabled>불러오는 중...</option>
                                                <template v-for="(items, group) in getScenarioGroups(profile.profileName)" :key="group">
                                                    <optgroup :label="group">
                                                        <option v-for="scenario in items" :key="scenario.id" :value="scenario.id">
                                                            {{ scenario.title }}
                                                        </option>
                                                    </optgroup>
                                                </template>
                                            </select>
                                            <div v-if="getScenarioStatus(profile.profileName)" class="text-xs text-red-400">
                                                {{ getScenarioStatus(profile.profileName) }}
                                            </div>
                                        </div>

                                        <div class="grid grid-cols-2 gap-2">
                                            <div class="space-y-1">
                                                <label class="text-xs text-zinc-400">턴 시간(분)</label>
                                                <select
                                                    v-model.number="profileInstalls[profile.profileName].turnTermMinutes"
                                                    class="w-full bg-zinc-950 border border-zinc-700 rounded px-3 py-2 text-sm text-white"
                                                >
                                                    <option v-for="term in turnTermOptions" :key="term" :value="term">
                                                        {{ term }}
                                                    </option>
                                                </select>
                                            </div>
                                            <div class="space-y-1">
                                                <label class="text-xs text-zinc-400">시간 동기화</label>
                                                <div class="flex gap-2">
                                                    <label class="flex items-center gap-1 text-xs text-zinc-300">
                                                        <input
                                                            v-model="profileInstalls[profile.profileName].sync"
                                                            class="accent-yellow-500"
                                                            type="radio"
                                                            :value="true"
                                                        />
                                                        Y
                                                    </label>
                                                    <label class="flex items-center gap-1 text-xs text-zinc-300">
                                                        <input
                                                            v-model="profileInstalls[profile.profileName].sync"
                                                            class="accent-yellow-500"
                                                            type="radio"
                                                            :value="false"
                                                        />
                                                        N
                                                    </label>
                                                </div>
                                            </div>
                                        </div>

                                        <div class="space-y-1">
                                            <label class="text-xs text-zinc-400">NPC 상성</label>
                                            <div class="flex gap-2">
                                                <label class="flex items-center gap-1 text-xs text-zinc-300">
                                                    <input
                                                        v-model.number="profileInstalls[profile.profileName].fiction"
                                                        class="accent-yellow-500"
                                                        type="radio"
                                                        :value="0"
                                                    />
                                                    연의
                                                </label>
                                                <label class="flex items-center gap-1 text-xs text-zinc-300">
                                                    <input
                                                        v-model.number="profileInstalls[profile.profileName].fiction"
                                                        class="accent-yellow-500"
                                                        type="radio"
                                                        :value="1"
                                                    />
                                                    가상
                                                </label>
                                            </div>
                                        </div>

                                        <div class="grid grid-cols-2 gap-2">
                                            <div class="space-y-1">
                                                <label class="text-xs text-zinc-400">확장 NPC</label>
                                                <div class="flex gap-2">
                                                    <label class="flex items-center gap-1 text-xs text-zinc-300">
                                                        <input
                                                            v-model="profileInstalls[profile.profileName].extend"
                                                            class="accent-yellow-500"
                                                            type="radio"
                                                            :value="true"
                                                        />
                                                        포함
                                                    </label>
                                                    <label class="flex items-center gap-1 text-xs text-zinc-300">
                                                        <input
                                                            v-model="profileInstalls[profile.profileName].extend"
                                                            class="accent-yellow-500"
                                                            type="radio"
                                                            :value="false"
                                                        />
                                                        미포함
                                                    </label>
                                                </div>
                                            </div>
                                            <div class="space-y-1">
                                                <label class="text-xs text-zinc-400">임관 모드</label>
                                                <div class="flex gap-2">
                                                    <label class="flex items-center gap-1 text-xs text-zinc-300">
                                                        <input
                                                            v-model="profileInstalls[profile.profileName].joinMode"
                                                            class="accent-yellow-500"
                                                            type="radio"
                                                            value="full"
                                                        />
                                                        일반
                                                    </label>
                                                    <label class="flex items-center gap-1 text-xs text-zinc-300">
                                                        <input
                                                            v-model="profileInstalls[profile.profileName].joinMode"
                                                            class="accent-yellow-500"
                                                            type="radio"
                                                            value="onlyRandom"
                                                        />
                                                        랜덤 임관
                                                    </label>
                                                </div>
                                            </div>
                                        </div>

                                        <div class="space-y-1">
                                            <label class="text-xs text-zinc-400">장수 임의 생성</label>
                                            <div class="flex gap-2 flex-wrap">
                                                <label class="flex items-center gap-1 text-xs text-zinc-300">
                                                    <input
                                                        v-model.number="profileInstalls[profile.profileName].blockGeneralCreate"
                                                        class="accent-yellow-500"
                                                        type="radio"
                                                        :value="0"
                                                    />
                                                    가능
                                                </label>
                                                <label class="flex items-center gap-1 text-xs text-zinc-300">
                                                    <input
                                                        v-model.number="profileInstalls[profile.profileName].blockGeneralCreate"
                                                        class="accent-yellow-500"
                                                        type="radio"
                                                        :value="2"
                                                    />
                                                    장수명 무작위
                                                </label>
                                                <label class="flex items-center gap-1 text-xs text-zinc-300">
                                                    <input
                                                        v-model.number="profileInstalls[profile.profileName].blockGeneralCreate"
                                                        class="accent-yellow-500"
                                                        type="radio"
                                                        :value="1"
                                                    />
                                                    불가
                                                </label>
                                            </div>
                                        </div>

                                        <div class="space-y-1">
                                            <label class="text-xs text-zinc-400">NPC 빙의</label>
                                            <div class="flex gap-2 flex-wrap">
                                                <label class="flex items-center gap-1 text-xs text-zinc-300">
                                                    <input
                                                        v-model.number="profileInstalls[profile.profileName].npcMode"
                                                        class="accent-yellow-500"
                                                        type="radio"
                                                        :value="1"
                                                    />
                                                    가능
                                                </label>
                                                <label class="flex items-center gap-1 text-xs text-zinc-300">
                                                    <input
                                                        v-model.number="profileInstalls[profile.profileName].npcMode"
                                                        class="accent-yellow-500"
                                                        type="radio"
                                                        :value="0"
                                                    />
                                                    불가
                                                </label>
                                                <label class="flex items-center gap-1 text-xs text-zinc-300">
                                                    <input
                                                        v-model.number="profileInstalls[profile.profileName].npcMode"
                                                        class="accent-yellow-500"
                                                        type="radio"
                                                        :value="2"
                                                    />
                                                    선택 생성 가능
                                                </label>
                                            </div>
                                        </div>
                                    </div>

                                    <div class="space-y-3">
                                        <div class="space-y-1">
                                            <label class="text-xs text-zinc-400">이미지 표기</label>
                                            <div class="flex gap-2 flex-wrap">
                                                <label class="flex items-center gap-1 text-xs text-zinc-300">
                                                    <input
                                                        v-model.number="profileInstalls[profile.profileName].showImgLevel"
                                                        class="accent-yellow-500"
                                                        type="radio"
                                                        :value="0"
                                                    />
                                                    안함
                                                </label>
                                                <label class="flex items-center gap-1 text-xs text-zinc-300">
                                                    <input
                                                        v-model.number="profileInstalls[profile.profileName].showImgLevel"
                                                        class="accent-yellow-500"
                                                        type="radio"
                                                        :value="1"
                                                    />
                                                    전콘
                                                </label>
                                                <label class="flex items-center gap-1 text-xs text-zinc-300">
                                                    <input
                                                        v-model.number="profileInstalls[profile.profileName].showImgLevel"
                                                        class="accent-yellow-500"
                                                        type="radio"
                                                        :value="2"
                                                    />
                                                    전콘, 병종
                                                </label>
                                                <label class="flex items-center gap-1 text-xs text-zinc-300">
                                                    <input
                                                        v-model.number="profileInstalls[profile.profileName].showImgLevel"
                                                        class="accent-yellow-500"
                                                        type="radio"
                                                        :value="3"
                                                    />
                                                    전콘, 병종, NPC
                                                </label>
                                            </div>
                                        </div>

                                        <div class="space-y-1">
                                            <label class="text-xs text-zinc-400">토너먼트 자동 시작</label>
                                            <div class="flex gap-2">
                                                <label class="flex items-center gap-1 text-xs text-zinc-300">
                                                    <input
                                                        v-model="profileInstalls[profile.profileName].tournamentTrig"
                                                        class="accent-yellow-500"
                                                        type="radio"
                                                        :value="false"
                                                    />
                                                    수동
                                                </label>
                                                <label class="flex items-center gap-1 text-xs text-zinc-300">
                                                    <input
                                                        v-model="profileInstalls[profile.profileName].tournamentTrig"
                                                        class="accent-yellow-500"
                                                        type="radio"
                                                        :value="true"
                                                    />
                                                    자동
                                                </label>
                                            </div>
                                        </div>

                                        <div class="space-y-1">
                                            <label class="text-xs text-zinc-400">휴식 턴 자동 행동</label>
                                            <div class="flex flex-wrap gap-2">
                                                <label
                                                    v-for="option in autorunOptionLabels"
                                                    :key="option.key"
                                                    class="flex items-center gap-1 text-xs text-zinc-300"
                                                >
                                                    <input
                                                        v-model="profileInstalls[profile.profileName].autorunUserOptions[option.key]"
                                                        class="accent-yellow-500"
                                                        type="checkbox"
                                                    />
                                                    {{ option.label }}
                                                </label>
                                            </div>
                                        </div>

                                        <div class="space-y-1">
                                            <label class="text-xs text-zinc-400">유효 시간(분)</label>
                                            <select
                                                v-model.number="profileInstalls[profile.profileName].autorunUserMinutes"
                                                class="w-full bg-zinc-950 border border-zinc-700 rounded px-3 py-2 text-sm text-white"
                                            >
                                                <option :value="0">꺼짐</option>
                                                <option :value="43200">항상</option>
                                                <option :value="10">10분</option>
                                                <option :value="20">20분</option>
                                                <option :value="30">30분</option>
                                                <option :value="60">1시간</option>
                                                <option :value="120">2시간</option>
                                                <option :value="180">3시간</option>
                                                <option :value="240">4시간</option>
                                                <option :value="360">6시간</option>
                                                <option :value="480">8시간</option>
                                                <option :value="600">10시간</option>
                                                <option :value="720">12시간</option>
                                                <option :value="1440">24시간</option>
                                                <option :value="2160">36시간</option>
                                                <option :value="2880">48시간</option>
                                                <option :value="3600">60시간</option>
                                                <option :value="4320">72시간</option>
                                            </select>
                                        </div>

                                        <div class="grid grid-cols-2 gap-2">
                                            <div class="space-y-1">
                                                <label class="text-xs text-zinc-400">오픈 예약</label>
                                                <input
                                                    v-model="profileInstalls[profile.profileName].openAt"
                                                    type="datetime-local"
                                                    class="w-full bg-zinc-950 border border-zinc-700 rounded px-3 py-2 text-sm text-white"
                                                />
                                            </div>
                                            <div class="space-y-1">
                                                <label class="text-xs text-zinc-400">가오픈 예약</label>
                                                <input
                                                    v-model="profileInstalls[profile.profileName].preopenAt"
                                                    type="datetime-local"
                                                    class="w-full bg-zinc-950 border border-zinc-700 rounded px-3 py-2 text-sm text-white"
                                                />
                                            </div>
                                        </div>

                                        <div class="space-y-1">
                                            <label class="text-xs text-zinc-400">설치 메모</label>
                                            <input
                                                v-model="profileInstalls[profile.profileName].reason"
                                                type="text"
                                                class="w-full bg-zinc-950 border border-zinc-700 rounded px-3 py-2 text-sm text-white"
                                                placeholder="사유/메모"
                                            />
                                        </div>

                                        <button
                                            class="bg-emerald-600 hover:bg-emerald-500 text-black font-semibold px-4 py-2 rounded w-full"
                                            @click="requestInstall(profile.profileName)"
                                        >
                                            설치 적용
                                        </button>

                                        <div v-if="getScenarioPreview(profile.profileName)" class="bg-zinc-950 border border-zinc-800 rounded p-3 text-xs text-zinc-300 space-y-2">
                                            <div class="font-semibold text-zinc-200">
                                                {{ getScenarioPreview(profile.profileName)?.title }}
                                            </div>
                                            <div>시작 연도: {{ getScenarioPreview(profile.profileName)?.year ?? '-' }}년</div>
                                            <div>
                                                NPC: {{ getScenarioPreview(profile.profileName)?.npcCount }}명
                                                <span v-if="getScenarioPreview(profile.profileName)?.npcExCount">+{{ getScenarioPreview(profile.profileName)?.npcExCount }}명</span>
                                                <span v-if="getScenarioPreview(profile.profileName)?.npcNeutralCount">
                                                    / 중립 {{ getScenarioPreview(profile.profileName)?.npcNeutralCount }}명
                                                </span>
                                            </div>
                                            <div class="space-y-1">
                                                <div class="text-zinc-400">국가</div>
                                                <div class="space-y-1">
                                                    <div
                                                        v-for="nation in getScenarioPreview(profile.profileName)?.nations ?? []"
                                                        :key="nation.id"
                                                        class="text-[11px]"
                                                    >
                                                        <span :style="{ color: nation.color }">{{ nation.name }}</span>
                                                        {{ nation.generals }}명
                                                        <span v-if="nation.generalsEx">(+{{ nation.generalsEx }})</span>
                                                        <span class="text-zinc-500">· {{ nation.cities.join(', ') }}</span>
                                                    </div>
                                                </div>
                                            </div>
                                        </div>
                                    </div>
                                </div>
                            </div>
                        </div>
                        <div v-if="profileActionStatus.global" class="text-xs text-red-400">
                            {{ profileActionStatus.global }}
                        </div>
                    </div>
                </section>
            </div>
        </div>
    </DefaultLayout>
</template>
