/**
 * 素材库页面
 * ==========
 *
 * 管理音频素材（音色克隆）。
 * 上传 mp3/wav → 七牛云 → Kling 音色克隆 → voices.json
 */

import { useState, useEffect, useRef, useCallback } from 'react';
import { useVoiceStore } from '../../store/voiceStore';
import { usePointStore } from '../../store';
import { toast } from '../../store/uiStore';
import { useProgressStore } from '../../store/progressStore';
import * as voiceApi from '../../api/modules/voice';
import Modal from '../../components/Modal/Modal';
import ConfirmModal from '../../components/Modal/ConfirmModal';
import RechargeModal from '../../components/RechargeModal/RechargeModal';
import './ContentManagement.css';

export default function VoiceMaterialLibrary() {
  const [uploadModalOpen, setUploadModalOpen] = useState(false);
  const [uploadName, setUploadName] = useState('');
  const [selectedFile, setSelectedFile] = useState<File | null>(null);

  // 重命名状态
  const [editingId, setEditingId] = useState<string | null>(null);
  const [editingName, setEditingName] = useState('');

  // 删除确认状态
  const [deleteModalOpen, setDeleteModalOpen] = useState(false);
  const [deleteTarget, setDeleteTarget] = useState<{ id: string; name: string } | null>(null);

  // 积分不足弹窗
  const [showPointsModal, setShowPointsModal] = useState(false);
  const showRechargeModal = usePointStore((state) => state.showRechargeModal);
  const setShowRechargeModal = usePointStore((state) => state.setShowRechargeModal);
  const fetchBalance = usePointStore((state) => state.fetchBalance);

  const fileInputRef = useRef<HTMLInputElement>(null);
  const pollingIds = useRef<Set<string>>(new Set());

  // 音频播放状态（全局单例）
  const [playingId, setPlayingId] = useState<string | null>(null);
  const audioRef = useRef<HTMLAudioElement | null>(null);

  // 清理音频（组件卸载或切换素材时）
  const stopAudio = useCallback(() => {
    if (audioRef.current) {
      audioRef.current.pause();
      audioRef.current.onended = null;
      audioRef.current = null;
    }
  }, []);

  // 切换播放/暂停
  const togglePlay = useCallback((id: string, url: string) => {
    // 情况1：点击当前正在播放的素材 -> 暂停
    if (playingId === id) {
      stopAudio();
      setPlayingId(null);
      return;
    }

    // 情况2：播放新素材 -> 先停止旧的
    stopAudio();
    setPlayingId(null);

    const audio = new Audio(url);
    audioRef.current = audio;

    // 播放自然结束
    audio.onended = () => {
      setPlayingId(prev => (prev === id ? null : prev));
      if (audioRef.current === audio) {audioRef.current = null;}
    };

    // 立即更新 UI，不给用户"闪烁"的感觉
    setPlayingId(id);

    // 开始播放（失败时回退）
    audio.play().catch(() => {
      setPlayingId(prev => (prev === id ? null : prev));
      if (audioRef.current === audio) {audioRef.current = null;}
    });
  }, [playingId, stopAudio]);

  // 组件卸载时清理
  useEffect(() => () => stopAudio(), [stopAudio]);

  const {
    voiceMaterials,
    isLoadingMaterials,
    loadVoiceMaterials,
    addVoiceMaterial,
    renameVoiceMaterial,
    deleteVoiceMaterial,
    updateVoiceMaterialStatus,
  } = useVoiceStore();

  // 加载数据
  useEffect(() => {
    loadVoiceMaterials();
  }, [loadVoiceMaterials]);

  // 轮询 pending/processing 状态的音频素材
  useEffect(() => {
    const pending = voiceMaterials.filter(m => m.status === 'pending' || m.status === 'processing');
    const intervals: ReturnType<typeof setInterval>[] = [];

    for (const item of pending) {
      if (pollingIds.current.has(item.id)) {continue;}
      pollingIds.current.add(item.id);

      const interval = setInterval(async () => {
        try {
          const result = await voiceApi.queryCloneTask(item.id);
          if (result.status === 'succeeded') {
            updateVoiceMaterialStatus(item.id, 'ready', result.voiceId, result.trialUrl);
            clearInterval(interval);
            pollingIds.current.delete(item.id);
          } else if (result.status === 'failed') {
            updateVoiceMaterialStatus(item.id, 'failed');
            clearInterval(interval);
            pollingIds.current.delete(item.id);
          }
        } catch (err) {
          console.error('[VoiceMaterialLibrary] 轮询克隆状态失败:', err);
        }
      }, 5000);

      intervals.push(interval);

      // 10 分钟后自动停止轮询
      setTimeout(() => {
        clearInterval(interval);
        pollingIds.current.delete(item.id);
      }, 600000);
    }

    return () => intervals.forEach(clearInterval);
  }, [voiceMaterials, updateVoiceMaterialStatus]);

  // 音频文件验证
  const validateAudioFile = (file: File): Promise<{ valid: boolean; error?: string }> => {
    return new Promise(resolve => {
      const maxSize = 20 * 1024 * 1024; // 20MB
      if (file.size > maxSize) {
        resolve({ valid: false, error: `文件大小 ${(file.size / 1024 / 1024).toFixed(1)}MB，要求不超过 20MB` });
        return;
      }

      const allowedExts = ['.mp3', '.m4a', '.wav'];
      const ext = file.name.substring(file.name.lastIndexOf('.')).toLowerCase();

      if (!allowedExts.includes(ext)) {
        resolve({ valid: false, error: '仅支持 MP3、M4A、WAV 格式' });
        return;
      }

      const audio = document.createElement('audio');
      audio.preload = 'metadata';

      audio.onloadedmetadata = () => {
        const duration = audio.duration;
        URL.revokeObjectURL(audio.src);
        if (duration < 10) {
          resolve({ valid: false, error: `音频时长 ${duration.toFixed(1)} 秒，要求至少 10 秒` });
          return;
        }
        if (duration > 300) {
          resolve({ valid: false, error: `音频时长 ${duration.toFixed(1)} 秒，要求不超过 5 分钟` });
          return;
        }
        resolve({ valid: true });
      };

      audio.onerror = () => {
        URL.revokeObjectURL(audio.src);
        resolve({ valid: false, error: '无法读取音频文件' });
      };

      setTimeout(() => {
        URL.revokeObjectURL(audio.src);
        resolve({ valid: false, error: '读取音频超时' });
      }, 8000);

      audio.src = URL.createObjectURL(file);
    });
  };

  // 文件选择
  const handleFileSelect = useCallback(async (e: React.ChangeEvent<HTMLInputElement>) => {
    const file = e.target.files?.[0];
    if (!file) {return;}

    const validation = await validateAudioFile(file);
    if (!validation.valid) {
      toast.error(validation.error || '文件验证失败');
      e.target.value = '';
      return;
    }

    setSelectedFile(file);
  }, []);

  // 上传处理
  const handleUpload = useCallback(async () => {
    if (!uploadName.trim() || !selectedFile) {return;}

    // 前置积分检查：声音复刻
    await fetchBalance();
    const voiceClonePoints = usePointStore.getState().getRule('voice_clone')?.points || 200;
    const balance = usePointStore.getState().balance;
    if (balance < voiceClonePoints) {
      setShowPointsModal(true);
      setUploadModalOpen(false);
      return;
    }

    const progress = useProgressStore.getState();
    setUploadModalOpen(false);

    progress.show('声音复刻');
    try {
      progress.update('文件校验中...');
      await addVoiceMaterial(selectedFile, uploadName.trim());
      progress.update('正在生成专属音色...');
      progress.success('复刻成功', 200);
    } catch (err) {
      const msg = err instanceof Error ? err.message : '上传失败';
      if (msg.includes('402') || msg.includes('积分不足')) {
        setShowPointsModal(true);
        progress.hide();
        return;
      }
      progress.error(msg);
    }

    setUploadName('');
    setSelectedFile(null);
  }, [uploadName, selectedFile, addVoiceMaterial, fetchBalance]);

  // 删除处理
  const openDeleteModal = (id: string, name: string) => {
    setDeleteTarget({ id, name });
    setDeleteModalOpen(true);
  };

  const handleConfirmDelete = useCallback(async () => {
    if (!deleteTarget) {return;}
    try {
      await deleteVoiceMaterial(deleteTarget.id);
      toast.success('已删除');
    } catch {
      toast.error('删除失败');
    } finally {
      setDeleteModalOpen(false);
      setDeleteTarget(null);
    }
  }, [deleteTarget, deleteVoiceMaterial]);

  const statusLabel = (status: string) => {
    switch (status) {
      case 'ready': return '可用';
      case 'pending': return '等待中';
      case 'processing': return '克隆中...';
      case 'failed': return '失败';
      default: return status;
    }
  };

  const startRename = (id: string, currentName: string) => {
    setEditingId(id);
    setEditingName(currentName);
  };

  const cancelRename = () => {
    setEditingId(null);
    setEditingName('');
  };

  const confirmRename = useCallback(async () => {
    if (!editingId || !editingName.trim()) {
      cancelRename();
      return;
    }
    try {
      await renameVoiceMaterial(editingId, editingName.trim());
      setEditingId(null);
      setEditingName('');
    } catch {
      toast.error('重命名失败');
    }
  }, [editingId, editingName, renameVoiceMaterial]);

  return (
    <div className="content-page">
      {/* 页面标题和上传区域 */}
      <div className="voice-clone-wrapper">
        <div className="voice-clone-title-group">
          <h2>声音复刻</h2>
          <p className="voice-clone-desc">上传人声音频，AI 自动学习声音特征，生成专属音色</p>
        </div>

        {/* 上传引导卡片 */}
        <div
          className="voice-upload-card"
          onClick={() => setUploadModalOpen(true)}
        >
          <div className="voice-upload-icon">
            <svg width="28" height="28" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round">
              <path d="M12 2a3 3 0 0 0-3 3v7a3 3 0 0 0 6 0V5a3 3 0 0 0-3-3z" />
              <path d="M19 10v2a7 7 0 0 1-14 0v-2" />
              <line x1="12" y1="19" x2="12" y2="22" />
              <line x1="8" y1="22" x2="16" y2="22" />
            </svg>
          </div>
          <div className="voice-upload-text">
            <span className="voice-upload-title">上传音频样本</span>
            <span className="voice-upload-hint">MP3 / M4A / WAV，建议时长 10 秒 ~ 5 分钟</span>
          </div>
          <div className="voice-upload-arrow">
            <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
              <line x1="12" y1="19" x2="12" y2="5" />
              <polyline points="5 12 12 5 19 12" />
            </svg>
          </div>
        </div>
      </div>

      {/* 上传弹窗 */}
      <Modal
        open={uploadModalOpen}
        onClose={() => setUploadModalOpen(false)}
        title=""
        width="480px"
      >
        <div style={{ display: 'flex', flexDirection: 'column', gap: 16 }}>
          <div>
            <label style={{ fontSize: 'var(--font-sm)', fontWeight: 500, marginBottom: 8, display: 'block' }}>
              音色名称
            </label>
            <input
              type="text"
              className="input"
              placeholder="例如：我的声音"
              value={uploadName}
              onChange={e => setUploadName(e.target.value)}
              style={{ width: '100%' }}
            />
          </div>

          <div>
            <label style={{ fontSize: 'var(--font-sm)', fontWeight: 500, marginBottom: 8, display: 'block' }}>
              选择文件
            </label>
            <div
              style={{
                border: '2px dashed var(--border-color)',
                borderRadius: 'var(--radius-md)',
                padding: 'var(--spacing-xl)',
                textAlign: 'center',
                cursor: 'pointer',
                transition: 'all var(--transition-fast)',
              }}
              onClick={() => fileInputRef.current?.click()}
              onMouseEnter={e => { e.currentTarget.style.borderColor = 'var(--primary)'; }}
              onMouseLeave={e => { e.currentTarget.style.borderColor = 'var(--border-color)'; }}
            >
              <input
                ref={fileInputRef}
                type="file"
                accept=".mp3,.m4a,.wav"
                onChange={handleFileSelect}
                style={{ display: 'none' }}
              />
              {selectedFile ? (
                <div>
                  <div style={{ fontWeight: 500, fontSize: 'var(--font-sm)' }}>{selectedFile.name}</div>
                  <div style={{ fontSize: 'var(--font-xs)', color: 'var(--text-secondary)', marginTop: 4 }}>
                    {(selectedFile.size / 1024 / 1024).toFixed(2)} MB
                  </div>
                </div>
              ) : (
                <div style={{ color: 'var(--text-secondary)' }}>
                  <div style={{ fontSize: 'var(--font-sm)' }}>点击选择文件</div>
                  <div style={{ fontSize: 'var(--font-xs)', marginTop: 6, lineHeight: 1.6 }}>
                    支持 MP3 / M4A / WAV，人声干净无杂音，时长 10 秒 ~ 5 分钟，不超过 20MB
                  </div>
                </div>
              )}
            </div>
          </div>

          <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
            <span style={{ fontSize: 'var(--font-xs)', color: 'var(--text-tertiary)' }}>
              每次复刻消耗 <strong style={{ color: 'var(--primary)' }}>200</strong> 积分
            </span>
            <div style={{ display: 'flex', gap: 12 }}>
              <button className="btn btn-secondary" onClick={() => setUploadModalOpen(false)}>取消</button>
              <button
                className="btn btn-primary"
                onClick={handleUpload}
                disabled={!uploadName.trim() || !selectedFile}
              >
                开始复刻
              </button>
            </div>
          </div>
        </div>
      </Modal>

      {/* 音频列表 */}
      {isLoadingMaterials ? (
        <p style={{ color: 'var(--text-secondary)' }}>加载中...</p>
      ) : voiceMaterials.length === 0 ? (
        <div className="empty-state-page">
          <div className="empty-state-icon">
            <svg width="48" height="48" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1" strokeLinecap="round" strokeLinejoin="round">
              <path d="M12 1a3 3 0 0 0-3 3v8a3 3 0 0 0 6 0V4a3 3 0 0 0-3-3z" />
              <path d="M19 10v2a7 7 0 0 1-14 0v-2" />
              <line x1="12" y1="19" x2="12" y2="23" />
              <line x1="8" y1="23" x2="16" y2="23" />
            </svg>
          </div>
          <p className="empty-state-title">暂无克隆音色</p>
          <p className="empty-state-desc">上传一段你的人声音频，<br />AI 将学习你的声音特征</p>
        </div>
      ) : (
        <div style={{ display: 'flex', flexDirection: 'column', gap: 16, flex: 1, overflow: 'auto' }}>
          <div className="voice-list" style={{ display: 'grid', gridTemplateColumns: 'repeat(2, 1fr)', gap: 12, alignContent: 'start', alignItems: 'start' }}>
            {voiceMaterials.map(m => (
              <div key={m.id} className="voice-row" style={{ cursor: 'default' }}>
                <div className="voice-row-main">
                  <div className="voice-row-info">
                    {editingId === m.id ? (
                      <input
                        type="text"
                        className="input"
                        value={editingName}
                        onChange={e => setEditingName(e.target.value)}
                        onKeyDown={e => {
                          if (e.key === 'Enter') {confirmRename();}
                          if (e.key === 'Escape') {cancelRename();}
                        }}
                        onBlur={confirmRename}
                        autoFocus
                        style={{ width: '100%', height: 28, padding: '2px 8px', fontSize: 'var(--font-sm)' }}
                      />
                    ) : (
                      <div className="voice-row-name">
                        {m.name}
                        <span className="voice-row-desc-inline">{statusLabel(m.status)}</span>
                      </div>
                    )}
                  </div>
                  <div className="voice-row-actions">
                    {m.sourceUrl && (
                      <button
                        className="preview-icon"
                        onClick={e => {
                          e.stopPropagation();
                          togglePlay(m.id, m.sourceUrl);
                        }}
                        title={playingId === m.id ? '暂停' : '播放'}
                      >
                        {playingId === m.id ? '⏸' : '▶'}
                      </button>
                    )}
                    <button
                      className="action-icon"
                      onClick={() => startRename(m.id, m.name)}
                      title="重命名"
                    >
                      <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={2} strokeLinecap="round" strokeLinejoin="round"><path d="M17 3a2.828 2.828 0 1 1 4 4L7.5 20.5 2 22l1.5-5.5L17 3z" /></svg>
                    </button>
                    <button
                      className="action-icon"
                      onClick={() => openDeleteModal(m.id, m.name)}
                      title="删除"
                    >
                      ✕
                    </button>
                  </div>
                </div>
              </div>
            ))}
          </div>

        </div>
      )}

      {/* 删除确认弹窗 */}
      <ConfirmModal
        open={deleteModalOpen}
        type="danger"
        title={<>确认删除素材 <strong>「{deleteTarget?.name}」</strong> 吗？</>}
        description="此操作不可撤销，素材将被永久删除"
        confirmText="确认删除"
        cancelText="取消"
        confirmButtonType="danger"
        onConfirm={handleConfirmDelete}
        onCancel={() => { setDeleteModalOpen(false); setDeleteTarget(null); }}
      />

      {/* 积分不足弹窗 */}
      <ConfirmModal
        open={showPointsModal}
        type="warning"
        title="积分不足"
        description="请先充值积分后再进行尝试"
        confirmText="立即充值"
        cancelText="稍后再说"
        confirmButtonType="danger"
        onConfirm={() => { setShowPointsModal(false); setShowRechargeModal(true); }}
        onCancel={() => setShowPointsModal(false)}
        onClose={() => setShowPointsModal(false)}
      />
      <RechargeModal
        open={showRechargeModal}
        onClose={() => setShowRechargeModal(false)}
        onRechargeSuccess={() => { fetchBalance(); setShowPointsModal(false); }}
      />
    </div>
  );
}
