/**
 * 듀얼 모니터 연동 — dashboard-260504.vercel.app 와 동일한 창 배치 로직
 * @see https://dashboard-260504.vercel.app/
 *
 * 핵심: 클릭 핸들러 안에서 await getScreenDetails() → 권한(모든 디스플레이) → window.open
 * localhost / IP(172.x) 동일 코드. IP는 origin별로 권한을 한 번 더 허용해야 함.
 */

const CHANNEL_NAME = 'eee_dashboard';
const DETAIL_WINDOW_NAME = 'eene_detail';
const SELECTED_TASK_KEY = 'eee_selected_task';
const SELECTED_STAGE_KEY = 'eee_selected_stage';
const PLACEMENT_STORAGE_KEY = 'eee_detail_window_placement';

export type DualMonitorEvent =
  | { type: 'TASK_SELECTED'; taskId: string; stageId?: string | null }
  | { type: 'TASK_DESELECTED' }
  | { type: 'REQUEST_SYNC' }
  | { type: 'REFRESH' };

let channel: BroadcastChannel | null = null;
let detailWindow: Window | null = null;
let dualModeActive = false;
let closePollTimer: ReturnType<typeof setInterval> | null = null;
let syncProvider: (() => string | null) | null = null;
let cachedPlacement: WindowPlacement | null = null;
let lastPlacementIssue: string | null = null;

export function getLastPlacementIssue(): string | null {
  return lastPlacementIssue;
}

/** http://IP 는 secure context가 아니어서 getScreenDetails·권한 팝업 불가 → https 필요 */
export function getWindowPlacementHint(): string | null {
  const win = window as WindowWithScreenDetails;
  if (win.getScreenDetails) return null;

  const { protocol, hostname } = window.location;
  const isLocalhost = hostname === 'localhost' || hostname === '127.0.0.1';

  if (protocol === 'http:' && !isLocalhost) {
    return (
      `http://${hostname} 으로는 「모든 디스플레이」 권한 창이 뜨지 않아 ` +
      `상세 창이 왼쪽 모니터에 열립니다.\n\n` +
      `→ https://${hostname}:3000 으로 접속해 주세요.\n` +
      `(인증서 경고 → 고급 → 계속)\n\n` +
      `localhost:3000 은 http 로도 동작합니다.`
    );
  }

  return '이 브라우저/환경에서는 다중 모니터 창 배치 API를 사용할 수 없습니다.';
}

interface ScreenDetailed {
  left: number;
  top: number;
  width: number;
  height: number;
  availLeft?: number;
  availTop?: number;
  availWidth?: number;
  availHeight?: number;
}

interface ScreenDetails {
  currentScreen: ScreenDetailed;
  screens: ScreenDetailed[];
}

interface WindowWithScreenDetails extends Window {
  getScreenDetails?: () => Promise<ScreenDetails>;
}

interface WindowPlacement {
  left: number;
  top: number;
  width: number;
  height: number;
}

function placementToFeatures({ left, top, width, height }: WindowPlacement): string {
  return `left=${left},top=${top},width=${width},height=${height},menubar=no,toolbar=no,location=no,status=no,resizable=yes,scrollbars=yes`;
}

function readPersistedPlacement(): WindowPlacement | null {
  if (cachedPlacement) return cachedPlacement;
  try {
    const raw = localStorage.getItem(PLACEMENT_STORAGE_KEY);
    if (!raw) return null;
    const p = JSON.parse(raw) as WindowPlacement;
    if (
      typeof p.left === 'number' &&
      typeof p.top === 'number' &&
      typeof p.width === 'number' &&
      typeof p.height === 'number'
    ) {
      cachedPlacement = p;
      return p;
    }
  } catch {
    /* ignore */
  }
  return null;
}

function persistPlacement(placement: WindowPlacement) {
  cachedPlacement = placement;
  try {
    localStorage.setItem(PLACEMENT_STORAGE_KEY, JSON.stringify(placement));
  } catch {
    /* ignore */
  }
}

function placementFromScreenDetails(details: ScreenDetails): WindowPlacement {
  const current = details.currentScreen;
  const sorted = [...details.screens].sort((a, b) => a.left - b.left || a.top - b.top);
  const idx = sorted.findIndex((s) => s.left === current.left && s.top === current.top);
  const target =
    (idx >= 0 && idx < sorted.length - 1 ? sorted[idx + 1] : null) ??
    sorted.find((s) => s.left !== current.left || s.top !== current.top) ??
    sorted[sorted.length - 1];

  return {
    left: target.availLeft ?? target.left,
    top: target.availTop ?? target.top,
    width: target.availWidth ?? target.width,
    height: target.availHeight ?? target.height,
  };
}

function fallbackDetailPlacement(): WindowPlacement {
  const stored = readPersistedPlacement();
  if (stored) return stored;

  const top = window.screen.availTop ?? 0;
  const height = window.screen.availHeight;
  const availLeft = window.screen.availLeft ?? 0;
  const availWidth = window.screen.availWidth;
  const monitorRight = availLeft + availWidth;

  return {
    left: monitorRight,
    top,
    width: Math.max(800, availWidth),
    height,
  };
}

/**
 * 참고 사이트와 동일 — getScreenDetails(await) 로 「모든 디스플레이」 권한 후 좌표 계산
 */
async function resolveDetailWindowPlacement(): Promise<WindowPlacement> {
  lastPlacementIssue = getWindowPlacementHint();

  try {
    const win = window as WindowWithScreenDetails;
    if (win.getScreenDetails) {
      const details = await win.getScreenDetails();
      lastPlacementIssue = null;
      return placementFromScreenDetails(details);
    }
  } catch (err) {
    console.warn('[dualMonitor] getScreenDetails failed, using fallback', err);
    lastPlacementIssue =
      '「모든 디스플레이」 권한이 거부되었거나 사용할 수 없습니다.\n' +
      '주소창 자물쇠 → 사이트 권한 → 창 배치 → 허용';
  }
  return fallbackDetailPlacement();
}

export async function getRightMonitorWindowFeatures(): Promise<string> {
  return placementToFeatures(await resolveDetailWindowPlacement());
}

function getChannel(): BroadcastChannel {
  if (!channel) {
    channel = new BroadcastChannel(CHANNEL_NAME);
  }
  return channel;
}

function stopClosePoll() {
  if (closePollTimer) {
    clearInterval(closePollTimer);
    closePollTimer = null;
  }
}

function startClosePoll(onClose: () => void) {
  stopClosePoll();
  closePollTimer = setInterval(() => {
    if (detailWindow && detailWindow.closed) {
      detailWindow = null;
      dualModeActive = false;
      stopClosePoll();
      onClose();
    }
  }, 500);
}

export function isDetailWindowOpen(): boolean {
  return !!detailWindow && !detailWindow.closed;
}

export function isDualModeActive(): boolean {
  return dualModeActive && isDetailWindowOpen();
}

function persistSelectedTask(taskId: string | null, stageId?: string | null) {
  if (taskId) {
    sessionStorage.setItem(SELECTED_TASK_KEY, taskId);
    if (stageId) sessionStorage.setItem(SELECTED_STAGE_KEY, stageId);
    else sessionStorage.removeItem(SELECTED_STAGE_KEY);
  } else {
    sessionStorage.removeItem(SELECTED_TASK_KEY);
    sessionStorage.removeItem(SELECTED_STAGE_KEY);
  }
}

export function getPersistedTaskId(): string | null {
  return sessionStorage.getItem(SELECTED_TASK_KEY);
}

export function getPersistedStageId(): string | null {
  return sessionStorage.getItem(SELECTED_STAGE_KEY);
}

export function registerSyncProvider(fn: () => string | null): () => void {
  syncProvider = fn;
  return () => {
    if (syncProvider === fn) syncProvider = null;
  };
}

function applyWindowPlacement(win: Window, left: number, top: number, width: number, height: number) {
  try {
    win.moveTo(left, top);
    win.resizeTo(width, height);
  } catch {
    /* ignore */
  }
}

function schedulePlacementRetries(win: Window, placement: WindowPlacement) {
  const apply = () => {
    if (win.closed) return;
    applyWindowPlacement(win, placement.left, placement.top, placement.width, placement.height);
  };
  apply();
  for (const delay of [100, 400, 1000, 2000]) {
    setTimeout(apply, delay);
  }
}

function postTaskSelected(taskId: string, stageId?: string | null) {
  getChannel().postMessage({ type: 'TASK_SELECTED', taskId, stageId } satisfies DualMonitorEvent);
}

function scheduleTaskSelected(taskId: string, stageId?: string | null) {
  postTaskSelected(taskId, stageId);
  setTimeout(() => postTaskSelected(taskId, stageId), 500);
  setTimeout(() => postTaskSelected(taskId, stageId), 1500);
}

function openDetailWindowWithPlacement(placement: WindowPlacement): Window | null {
  const detailUrl = `${window.location.origin}/detail?view=detail`;
  const features = placementToFeatures(placement);

  detailWindow = window.open(detailUrl, DETAIL_WINDOW_NAME, features);
  if (!detailWindow) {
    console.warn('상세 창을 열지 못했습니다. 브라우저 팝업 허용을 확인해 주세요.');
    return null;
  }

  try {
    detailWindow.focus();
  } catch {
    /* ignore */
  }

  applyWindowPlacement(detailWindow, placement.left, placement.top, placement.width, placement.height);
  return detailWindow;
}

/** 참고 사이트: getScreenDetails(await) → window.open */
async function openDetailWindowPlaced(onPopupClosed?: () => void): Promise<Window | null> {
  const placement = await resolveDetailWindowPlacement();
  persistPlacement(placement);

  const win = openDetailWindowWithPlacement(placement);
  if (!win) return null;

  startClosePoll(() => onPopupClosed?.());
  schedulePlacementRetries(win, placement);
  return win;
}

/** 듀얼뷰 토글 */
export async function openDetailWindow(onPopupClosed?: () => void): Promise<Window | null> {
  if (isDetailWindowOpen()) {
    detailWindow!.close();
    detailWindow = null;
    dualModeActive = false;
    stopClosePoll();
    onPopupClosed?.();
    return null;
  }

  dualModeActive = true;
  const win = await openDetailWindowPlaced(onPopupClosed);
  if (!win) {
    dualModeActive = false;
    return null;
  }

  const savedTaskId = getPersistedTaskId();
  const savedStageId = getPersistedStageId();
  if (savedTaskId) scheduleTaskSelected(savedTaskId, savedStageId);
  return win;
}

/** 업무 선택 — await getScreenDetails(권한) 후 팝업 open */
export async function sendTaskSelected(
  taskId: string,
  stageId?: string | null,
  onPopupClosed?: () => void,
): Promise<boolean> {
  persistSelectedTask(taskId, stageId);

  if (!isDetailWindowOpen()) {
    dualModeActive = true;
    const win = await openDetailWindowPlaced(onPopupClosed);
    if (!win) {
      dualModeActive = false;
      return false;
    }
    scheduleTaskSelected(taskId, stageId);
    if (lastPlacementIssue) {
      window.alert(lastPlacementIssue);
    }
    return true;
  }

  scheduleTaskSelected(taskId, stageId);

  try {
    detailWindow!.focus();
  } catch {
    /* ignore */
  }

  const placement = await resolveDetailWindowPlacement();
  persistPlacement(placement);
  if (detailWindow && !detailWindow.closed) {
    schedulePlacementRetries(detailWindow, placement);
  }

  return true;
}

export function sendTaskDeselected(): void {
  persistSelectedTask(null);
  getChannel().postMessage({ type: 'TASK_DESELECTED' } satisfies DualMonitorEvent);
}

export function requestDetailSync(): void {
  getChannel().postMessage({ type: 'REQUEST_SYNC' } satisfies DualMonitorEvent);
}

function respondToSyncRequest() {
  const id = syncProvider?.() ?? getPersistedTaskId();
  if (id) postTaskSelected(id, getPersistedStageId());
  else getChannel().postMessage({ type: 'TASK_DESELECTED' } satisfies DualMonitorEvent);
}

export function onDualMonitorEvent(
  handler: (event: DualMonitorEvent) => void,
  options?: { isPopupView?: boolean },
): () => void {
  const ch = getChannel();
  const isPopupView = options?.isPopupView ?? false;

  const listener = (e: MessageEvent<DualMonitorEvent>) => {
    const evt = e.data;
    if (evt.type === 'REQUEST_SYNC') {
      if (!isPopupView) respondToSyncRequest();
      return;
    }
    handler(evt);
  };

  ch.addEventListener('message', listener);

  if (isPopupView) {
    requestDetailSync();
  }

  return () => ch.removeEventListener('message', listener);
}

export function closeChannel(): void {
  stopClosePoll();
  channel?.close();
  channel = null;
}

export function openLinkOnRightMonitor(url: string, windowName: string): Window | null {
  const win = window.open(url, windowName, 'noopener,noreferrer,width=1280,height=900');
  try {
    win?.focus();
  } catch {
    /* ignore */
  }

  if (win) {
    void resolveDetailWindowPlacement().then((placement) => {
      schedulePlacementRetries(win, placement);
    });
  }

  return win;
}
