import { create } from 'zustand';
import type { CreationMode, ModelOption, AspectRatio, Duration, Resolution, GenerationType, UploadedFile } from '../types';
import { showToast } from '../components/Toast';
import { mediaApi } from '../lib/api';
import { parseAssetMentions } from '../lib/assetMentions';

let fileCounter = 0;

/** Read image dimensions via HTML5 Image element. */
function getImageDimensions(file: File): Promise<{ width: number; height: number }> {
  return new Promise((resolve, reject) => {
    const img = new Image();
    const url = URL.createObjectURL(file);
    const cleanup = () => URL.revokeObjectURL(url);
    const timeout = setTimeout(() => { cleanup(); reject(new Error('timeout')); }, 10000);
    img.onload = () => {
      clearTimeout(timeout);
      resolve({ width: img.naturalWidth, height: img.naturalHeight });
      cleanup();
    };
    img.onerror = () => {
      clearTimeout(timeout);
      reject(new Error('无法读取图片'));
      cleanup();
    };
    img.src = url;
  });
}

/** Read duration (and dimensions for video) of an audio or video file. */
interface MediaInfo {
  duration: number;
  width?: number;
  height?: number;
}

function getMediaInfo(file: File): Promise<MediaInfo> {
  return new Promise((resolve, reject) => {
    const isAudio = file.type.startsWith('audio/');
    const el = isAudio ? new Audio() : document.createElement('video');
    const url = URL.createObjectURL(file);
    const cleanup = () => URL.revokeObjectURL(url);
    const timeout = setTimeout(() => { cleanup(); reject(new Error('timeout')); }, 10000);
    el.addEventListener('loadedmetadata', () => {
      clearTimeout(timeout);
      if (isAudio) {
        resolve({ duration: el.duration });
      } else {
        const vid = el as HTMLVideoElement;
        resolve({ duration: vid.duration, width: vid.videoWidth, height: vid.videoHeight });
      }
      cleanup();
    });
    el.addEventListener('error', () => {
      clearTimeout(timeout);
      reject(new Error('无法读取媒体文件'));
      cleanup();
    });
    el.src = url;
  });
}

// API limits per Seedance 2.0 official doc
const MAX_IMAGES = 9;
const MAX_VIDEOS = 3;
const MAX_AUDIO = 3;

interface InputBarState {
  // Generation type
  generationType: GenerationType;
  setGenerationType: (type: GenerationType) => void;

  // Mode
  mode: CreationMode;
  setMode: (mode: CreationMode) => void;

  // Model
  model: ModelOption;
  setModel: (model: ModelOption) => void;

  // Aspect ratio
  aspectRatio: AspectRatio;
  setAspectRatio: (ratio: AspectRatio) => void;
  prevAspectRatio: AspectRatio;

  // Duration
  duration: Duration;
  setDuration: (duration: Duration) => void;
  prevDuration: Duration;

  // Resolution (480p/720p/1080p) — 1080p 仅 Seedance 2.0 支持
  resolution: Resolution;
  setResolution: (resolution: Resolution) => void;

  // Prompt
  prompt: string;
  setPrompt: (prompt: string) => void;

  // Editor HTML (rich content with mention tags)
  editorHtml: string;
  setEditorHtml: (html: string) => void;

  // Universal references
  references: UploadedFile[];
  prevReferences: UploadedFile[];
  addReferences: (files: File[]) => void;
  removeReference: (id: string) => void;
  clearReferences: () => void;
  retryUpload: (refId: string) => void;

  // Keyframe
  firstFrame: UploadedFile | null;
  lastFrame: UploadedFile | null;
  setFirstFrame: (file: File | null) => void;
  setLastFrame: (file: File | null) => void;

  // Computed
  canSubmit: () => boolean;

  // Search mode (联网搜索)
  searchMode: 'smart' | 'off';
  setSearchMode: (mode: 'smart' | 'off') => void;

  // Seed (种子值)
  seed: number;
  seedEnabled: boolean;
  setSeed: (seed: number) => void;
  setSeedEnabled: (enabled: boolean) => void;

  // Asset mentions (for reEdit/regenerate to pass asset data to PromptInput rebuild)
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
  assetMentions: Record<string, any>[];

  // @ trigger (for toolbar button to insert @ in contentEditable)
  insertAtTrigger: number;
  triggerInsertAt: () => void;

  // Actions
  switchMode: (mode: CreationMode) => void;
  submit: () => void;
  reset: () => void;
}

export const useInputBarStore = create<InputBarState>((set, get) => ({
  generationType: 'video',
  setGenerationType: (generationType) => set({ generationType }),

  mode: 'universal',
  setMode: (mode) => set({ mode }),

  model: 'seedance_2.0',
  setModel: (model) => {
    // Fast + 1080P 为非法组合（官方文档约束）。UI Dropdown 已对 Fast 项置灰，
    // 此处为 UI 被绕过时的防御性拦截（depth defense），不做静默降级：
    // 阻止切换 + toast 引导用户手动改分辨率，让用户选择始终被尊重。
    const state = get();
    if (model === 'seedance_2.0_fast' && state.resolution === '1080p') {
      showToast('1080P 仅 AirDrama 模型支持，请先切换分辨率到 720P 或 480P');
      return;
    }
    set({ model });
  },

  aspectRatio: '21:9',
  setAspectRatio: (aspectRatio) => set({ aspectRatio, prevAspectRatio: aspectRatio }),
  prevAspectRatio: '21:9',

  duration: 15,
  setDuration: (duration) => {
    const state = get();
    if (state.mode === 'universal') {
      set({ duration, prevDuration: duration });
    } else {
      set({ duration });
    }
  },
  prevDuration: 15,

  resolution: '720p' as Resolution,
  setResolution: (resolution) => {
    // Fast + 1080P 非法组合（对称 setModel 的拦截）— UI Dropdown 已置灰，此处防御性拦截
    const state = get();
    if (resolution === '1080p' && state.model === 'seedance_2.0_fast') {
      showToast('AirDrama Fast 不支持 1080P，请先切换模型到 AirDrama');
      return;
    }
    set({ resolution });
  },

  prompt: '',
  setPrompt: (prompt) => set({ prompt }),

  editorHtml: '',
  setEditorHtml: (editorHtml) => set({ editorHtml }),

  references: [],
  prevReferences: [],
  addReferences: (files) => {
    const state = get();
    // Count existing references by type + merge @ asset mentions
    const counts = { image: 0, video: 0, audio: 0 };
    for (const ref of state.references) counts[ref.type]++;
    const { counts: assetCounts } = parseAssetMentions(state.editorHtml);
    counts.image += assetCounts.image;
    counts.video += assetCounts.video;
    counts.audio += assetCounts.audio;

    // Separate images (sync) from audio/video (need async duration check)
    const imageFiles: File[] = [];
    const mediaFiles: File[] = []; // audio + video

    for (const file of files) {
      const type: 'image' | 'video' | 'audio' = file.type.startsWith('video/')
        ? 'video'
        : file.type.startsWith('audio/')
          ? 'audio'
          : 'image';

      const max = type === 'image' ? MAX_IMAGES : type === 'video' ? MAX_VIDEOS : MAX_AUDIO;
      if (counts[type] >= max) {
        const label = type === 'image' ? `最多上传${MAX_IMAGES}张图片` : type === 'video' ? `最多上传${MAX_VIDEOS}个视频` : `最多上传${MAX_AUDIO}个音频`;
        showToast(label);
        continue;
      }
      counts[type]++;

      if (type === 'image') {
        imageFiles.push(file);
      } else {
        mediaFiles.push(file);
      }
    }

    // ── Async: validate image dimensions, then add + upload ──
    if (imageFiles.length > 0) {
      _validateAndAddImages(imageFiles);
    }

    // ── Async: validate audio/video duration, then add + upload ──
    if (mediaFiles.length > 0) {
      _validateAndAddMedia(mediaFiles);
    }
  },
  removeReference: (id) => {
    const state = get();
    const removedRef = state.references.find((r) => r.id === id);
    if (!removedRef) return;
    if (removedRef.previewUrl) URL.revokeObjectURL(removedRef.previewUrl);

    // 删除后同类型的剩余引用按顺序重新编号（即梦逻辑：保持 1/2/3 连续）
    const remaining = state.references.filter((r) => r.id !== id);
    const labelPrefix = removedRef.type === 'image' ? '图片' : removedRef.type === 'video' ? '视频' : '音频';
    const labelUpdates = new Map<string, string>(); // refId -> newLabel
    let idx = 1;
    const relabeled = remaining.map((r) => {
      if (r.type !== removedRef.type) return r;
      const newLabel = `${labelPrefix}${idx++}`;
      if (r.label !== newLabel) labelUpdates.set(r.id, newLabel);
      return r.label === newLabel ? r : { ...r, label: newLabel };
    });

    // 同步更新 editorHtml 里对应 refId 的 @mention span 文本
    let newEditorHtml = state.editorHtml;
    if (labelUpdates.size > 0 && newEditorHtml) {
      const doc = new DOMParser().parseFromString(`<div>${newEditorHtml}</div>`, 'text/html');
      const container = doc.body.firstChild as HTMLElement | null;
      if (container) {
        container.querySelectorAll('[data-ref-id]').forEach((span) => {
          const el = span as HTMLElement;
          const refId = el.dataset.refId;
          if (refId && labelUpdates.has(refId)) {
            const newLabel = labelUpdates.get(refId)!;
            // span 结构：[icon/img] + atHidden(@) + textNode(label)
            const labelNode = [...el.childNodes].reverse().find((n) => n.nodeType === Node.TEXT_NODE);
            if (labelNode) labelNode.textContent = newLabel;
          }
        });
        newEditorHtml = container.innerHTML;
      }
    }

    set({ references: relabeled, editorHtml: newEditorHtml });
  },
  clearReferences: () => {
    const state = get();
    state.references.forEach((r) => URL.revokeObjectURL(r.previewUrl));
    set({ references: [] });
  },
  retryUpload: (refId) => {
    const ref = get().references.find((r) => r.id === refId);
    if (!ref?.file) return;
    set({
      references: get().references.map((r) =>
        r.id === refId ? { ...r, uploading: true, uploadError: false } : r
      ),
    });
    _uploadRef(refId, ref.file);
  },

  firstFrame: null,
  lastFrame: null,
  setFirstFrame: (file) => {
    const state = get();
    if (state.firstFrame) URL.revokeObjectURL(state.firstFrame.previewUrl);
    if (file) {
      fileCounter++;
      set({
        firstFrame: {
          id: `first_${fileCounter}`,
          file,
          type: 'image',
          previewUrl: URL.createObjectURL(file),
          label: '首帧',
        },
      });
    } else {
      set({ firstFrame: null });
    }
  },
  setLastFrame: (file) => {
    const state = get();
    if (state.lastFrame) URL.revokeObjectURL(state.lastFrame.previewUrl);
    if (file) {
      fileCounter++;
      set({
        lastFrame: {
          id: `last_${fileCounter}`,
          file,
          type: 'image',
          previewUrl: URL.createObjectURL(file),
          label: '尾帧',
        },
      });
    } else {
      set({ lastFrame: null });
    }
  },

  canSubmit: () => {
    const state = get();
    const hasText = state.prompt.trim().length > 0;
    const hasFiles =
      state.mode === 'universal'
        ? state.references.length > 0
        : state.firstFrame !== null || state.lastFrame !== null;
    if (!hasText && !hasFiles) return false;
    // Audio cannot be the only reference — Seedance API requires image or video alongside
    if (state.mode === 'universal') {
      const hasAudioRef = state.references.some((r) => r.type === 'audio');
      const hasAudioAsset = (state.assetMentions || []).some((m: Record<string, string>) =>
        (m.assetType || '').toLowerCase() === 'audio');
      if (hasAudioRef || hasAudioAsset) {
        const hasImageOrVideoRef = state.references.some((r) => r.type === 'image' || r.type === 'video');
        const hasImageOrVideoAsset = (state.assetMentions || []).some((m: Record<string, string>) => {
          const t = (m.assetType || '').toLowerCase();
          return t === 'image' || t === 'video';
        });
        if (!hasImageOrVideoRef && !hasImageOrVideoAsset) return false;
      }
    }
    // Block submit if any reference is still uploading or failed
    if (state.references.some((r) => r.uploading || r.uploadError)) return false;
    return true;
  },

  searchMode: 'off',
  setSearchMode: (searchMode) => set({ searchMode }),

  seed: -1,
  seedEnabled: false,
  setSeed: (seed) => set({ seed }),
  setSeedEnabled: (seedEnabled) => set({ seedEnabled, seed: -1 }),

  assetMentions: [],

  insertAtTrigger: 0,
  triggerInsertAt: () => set((s) => ({ insertAtTrigger: s.insertAtTrigger + 1 })),

  switchMode: (mode) => {
    const state = get();
    if (state.mode === mode) return;

    if (mode === 'keyframe') {
      set({
        mode,
        prevReferences: state.references,
        references: [],
        assetMentions: [],
        aspectRatio: '16:9',
        duration: 5,
      });
    } else {
      // Clear keyframe
      if (state.firstFrame) URL.revokeObjectURL(state.firstFrame.previewUrl);
      if (state.lastFrame) URL.revokeObjectURL(state.lastFrame.previewUrl);
      set({
        mode,
        assetMentions: [],
        firstFrame: null,
        lastFrame: null,
        references: state.prevReferences,
        prevReferences: [],
        aspectRatio: state.prevAspectRatio,
        duration: state.prevDuration,
      });
    }
  },

  submit: () => {
    // Just show a toast - no real API call
  },

  reset: () => {
    const state = get();
    state.references.forEach((r) => URL.revokeObjectURL(r.previewUrl));
    state.prevReferences.forEach((r) => URL.revokeObjectURL(r.previewUrl));
    if (state.firstFrame) URL.revokeObjectURL(state.firstFrame.previewUrl);
    if (state.lastFrame) URL.revokeObjectURL(state.lastFrame.previewUrl);
    set({
      mode: 'universal',
      model: 'seedance_2.0',
      aspectRatio: '21:9',
      prevAspectRatio: '21:9',
      duration: 15,
      prevDuration: 15,
      resolution: '720p',
      prompt: '',
      editorHtml: '',
      references: [],
      prevReferences: [],
      assetMentions: [],
      firstFrame: null,
      lastFrame: null,
      generationType: 'video',
    });
  },
}));

// ── Module-level helpers (use useInputBarStore late-binding) ──

/** Upload a single reference file to TOS and update the store. */
function _uploadRef(refId: string, file: File) {
  mediaApi.upload(file).then(({ data }) => {
    useInputBarStore.setState((s) => ({
      references: s.references.map((r) =>
        r.id === refId ? { ...r, tosUrl: data.url, uploading: false, uploadError: false } : r
      ),
    }));
  }).catch(() => {
    useInputBarStore.setState((s) => ({
      references: s.references.map((r) =>
        r.id === refId ? { ...r, uploading: false, uploadError: true } : r
      ),
    }));
  });
}

/** Validate image dimensions per Seedance API spec, then add to store + start upload. */
async function _validateAndAddImages(files: File[]) {
  for (const file of files) {
    // Read dimensions
    let dims: { width: number; height: number };
    try {
      dims = await getImageDimensions(file);
    } catch {
      showToast('无法读取图片信息');
      continue;
    }

    const { width, height } = dims;
    // API spec: width/height in open interval (300, 6000)
    if (width >= 6000 || height >= 6000) {
      showToast(`图片尺寸过大（${width}×${height}），宽高需小于 6000 像素`);
      continue;
    }
    if (width <= 300 || height <= 300) {
      showToast(`图片尺寸过小（${width}×${height}），宽高需大于 300 像素`);
      continue;
    }
    // API spec: aspect ratio (width/height) in open interval (0.4, 2.5)
    const ratio = width / height;
    if (ratio <= 0.4 || ratio >= 2.5) {
      showToast(`图片比例不支持（${width}×${height}），宽高比需在 0.4 到 2.5 之间`);
      continue;
    }

    // Re-check count
    const state = useInputBarStore.getState();
    const currentCount = state.references.filter((r) => r.type === 'image').length;
    if (currentCount >= MAX_IMAGES) {
      showToast(`最多上传${MAX_IMAGES}张图片`);
      continue;
    }

    // Passed — add to store + upload
    fileCounter++;
    const existingSameType = state.references.filter(r => r.type === 'image').length
      + (state.assetMentions || []).filter((m: Record<string, string>) => m.type === 'image').length;
    const refId = `ref_${fileCounter}`;
    const newRef: UploadedFile = {
      id: refId,
      file,
      type: 'image',
      previewUrl: URL.createObjectURL(file),
      label: `图片${existingSameType + 1}`,
      uploading: true,
    };

    useInputBarStore.setState((s) => ({
      references: [...s.references, newRef],
    }));

    _uploadRef(refId, file);
  }
}

const MAX_MEDIA_DURATION = 15; // seconds per item and total

/** Validate audio/video duration (+ video dimensions), then add to store + start upload. */
async function _validateAndAddMedia(files: File[]) {
  for (const file of files) {
    const type: 'video' | 'audio' = file.type.startsWith('video/') ? 'video' : 'audio';
    const typeLabel = type === 'video' ? '视频' : '音频';

    // Read duration (+ dimensions for video)
    let info: MediaInfo;
    try {
      info = await getMediaInfo(file);
    } catch {
      showToast(`无法读取${typeLabel}文件信息`);
      continue;
    }

    const dur = info.duration;

    // Single item duration check
    // API specifies [2, 15]s — lower bound strict, upper bound +0.4s for codec imprecision
    if (dur < 2) {
      showToast(`${typeLabel}时长不能少于2秒`);
      continue;
    }
    if (dur > MAX_MEDIA_DURATION + 0.4) {
      showToast(`单条${typeLabel}时长不能超过${MAX_MEDIA_DURATION}秒`);
      continue;
    }

    // Video dimension checks — API spec: [300, 6000]px, ratio [0.4, 2.5], pixels [409600, 927408]
    if (type === 'video' && info.width && info.height) {
      const { width, height } = info;
      if (width > 6000 || height > 6000) {
        showToast(`视频尺寸过大（${width}×${height}），宽高不能超过 6000 像素`);
        continue;
      }
      if (width < 300 || height < 300) {
        showToast(`视频尺寸过小（${width}×${height}），宽高不能小于 300 像素`);
        continue;
      }
      const ratio = width / height;
      if (ratio < 0.4 || ratio > 2.5) {
        showToast(`视频比例不支持（${width}×${height}），宽高比需在 0.4 到 2.5 之间`);
        continue;
      }
      const pixels = width * height;
      if (pixels < 409600) {
        showToast(`视频像素过低（${width}×${height}=${pixels.toLocaleString()}），最低需 409,600 像素`);
        continue;
      }
      if (pixels > 927408) {
        showToast(`视频像素过高（${width}×${height}=${pixels.toLocaleString()}），最高 927,408 像素`);
        continue;
      }
    }

    // Total duration check (same type) — merge @ asset mention durations
    const state = useInputBarStore.getState();
    const { durations: assetDurations } = parseAssetMentions(state.editorHtml);
    const refDuration = state.references
      .filter((r) => r.type === type && r.duration)
      .reduce((sum, r) => sum + (r.duration || 0), 0);
    const existingDuration = refDuration + (type === 'video' ? assetDurations.video : assetDurations.audio);
    if (existingDuration + dur > MAX_MEDIA_DURATION + 0.4) {
      showToast(`${typeLabel}总时长不能超过${MAX_MEDIA_DURATION}秒`);
      continue;
    }

    // Re-check count (may have changed since initial check)
    const currentCount = state.references.filter((r) => r.type === type).length;
    const max = type === 'video' ? MAX_VIDEOS : MAX_AUDIO;
    if (currentCount >= max) {
      showToast(`最多上传${max}个${typeLabel}`);
      continue;
    }

    // Passed all checks — add to store
    fileCounter++;
    const labelPrefix = type === 'video' ? '视频' : '音频';
    const existingSameType = state.references.filter(r => r.type === type).length
      + (state.assetMentions || []).filter((m: Record<string, string>) => m.type === type).length;
    const refId = `ref_${fileCounter}`;
    const newRef: UploadedFile = {
      id: refId,
      file,
      type,
      previewUrl: type === 'audio' ? '' : URL.createObjectURL(file),
      label: `${labelPrefix}${existingSameType + 1}`,
      uploading: true,
      duration: Math.round(dur * 10) / 10,
    };

    useInputBarStore.setState((s) => ({
      references: [...s.references, newRef],
    }));

    // Start upload
    _uploadRef(refId, file);
  }
}
