import React, { useMemo, useState } from 'react';
import { createRoot } from 'react-dom/client';
import { Flame, Map, Package, RotateCcw, Save, ScrollText, Swords } from 'lucide-react';
import './style.css';

const SAVE_KEY = 'qinglan-wuxiang-save-v2';
const PLAYER_KEY = 'qinglan-wuxiang-player-id';
const API_BASE = import.meta.env.VITE_API_BASE || '';
const canUseCloudSave = Boolean(API_BASE);

const GAME = {
  player: {
    name: '沈砚', hp: 150, maxHp: 150, mp: 70, maxMp: 70, atk: 18, def: 8,
    lvl: 1, xp: 0, gold: 120, chapter: 0,
    equip: { weapon: '桃木剑', armor: '粗布道袍', trinket: '无' },
    inv: { 金疮药: 6, 回气散: 4, 辟谷丹: 3, 清心符: 1 },
  },
  items: {
    金疮药: { kind: '药', desc: '恢复45点气血', hp: 45 },
    回气散: { kind: '药', desc: '恢复35点真元', mp: 35 },
    辟谷丹: { kind: '药', desc: '恢复25点气血与20点真元', hp: 25, mp: 20 },
    凝血丸: { kind: '药', desc: '恢复90点气血', hp: 90 },
    醒神露: { kind: '药', desc: '恢复80点真元', mp: 80 },
    归元丹: { kind: '药', desc: '气血真元全部回满', full: true },
    清心符: { kind: '符', desc: '战斗中恢复35点气血', combat: true, hp: 35 },
    火鸦符: { kind: '符', desc: '战斗中造成55点火焰伤害', combat: true, damage: 55 },
    玄霜符: { kind: '符', desc: '战斗中造成90点寒霜伤害', combat: true, damage: 90 },
    铁鳞丹: { kind: '药', desc: '本场战斗护体+8', combat: true, buffDef: 8 },
    龙涎香: { kind: '材', desc: '渡口支线信物，可卖可留' },
    残玉: { kind: '证物', desc: '无相教残玉，推动主线的证物' },
    赤蛟鳞: { kind: '材', desc: '灼热如炭的蛟鳞' },
    星砂: { kind: '材', desc: '天墟集通用的稀有材料' },
  },
  gear: {
    桃木剑: { slot: 'weapon', atk: 3 }, 青钢剑: { slot: 'weapon', atk: 8 },
    霜纹剑: { slot: 'weapon', atk: 15, maxMp: 10 }, 玄铁尺: { slot: 'weapon', atk: 24, def: 3 },
    粗布道袍: { slot: 'armor', def: 2 }, 云纹袍: { slot: 'armor', def: 8, maxMp: 15 },
    鳞甲道衣: { slot: 'armor', def: 14, maxHp: 40 }, 太乙鹤氅: { slot: 'armor', def: 21, maxMp: 35 },
    无: { slot: 'trinket' }, 兽牙坠: { slot: 'trinket', atk: 4 },
    镇魂铃: { slot: 'trinket', def: 4, maxMp: 20 }, 星河佩: { slot: 'trinket', atk: 8, def: 5, maxHp: 30 },
  },
  skills: [
    { name: '斩风', mp: 0, power: 1, text: '稳健一剑' },
    { name: '离火诀', mp: 12, power: 1.8, text: '掌心火符炸开' },
    { name: '听雨剑', mp: 18, power: 2.4, text: '剑势如雨连绵', lvl: 3 },
    { name: '雷引', mp: 28, power: 3.2, text: '引雷破邪', lvl: 5 },
    { name: '太虚归元', mp: 38, power: 2.1, healRate: 0.35, text: '伤敌并回气血', lvl: 7 },
  ],
  shops: {
    青岚镇: [['金疮药', 20], ['回气散', 22], ['火鸦符', 45], ['青钢剑', 120], ['云纹袍', 110]],
    栖霞渡: [['辟谷丹', 28], ['铁鳞丹', 40], ['醒神露', 65], ['霜纹剑', 260], ['镇魂铃', 220]],
    沉剑关: [['凝血丸', 70], ['玄霜符', 90], ['玄铁尺', 520], ['鳞甲道衣', 480]],
    天墟集: [['归元丹', 180], ['太乙鹤氅', 900], ['星河佩', 760]],
  },
  mobs: {
    wolf: { name: '月牙狼', hp: 75, atk: 13, def: 4, xp: 35, gold: 22, drop: ['金疮药'] },
    bandit: { name: '黑风寨哨探', hp: 95, atk: 17, def: 6, xp: 50, gold: 38, drop: ['火鸦符'] },
    ghost: { name: '破庙伥鬼', hp: 110, atk: 20, def: 5, xp: 70, gold: 45, drop: ['清心符', '残玉'] },
    snake: { name: '毒沼青鳞蛇', hp: 140, atk: 24, def: 8, xp: 95, gold: 62, drop: ['铁鳞丹', '龙涎香'] },
    guard: { name: '沉剑关叛军', hp: 165, atk: 30, def: 12, xp: 125, gold: 90, drop: ['凝血丸'] },
    demon: { name: '镜湖水魈', hp: 210, atk: 36, def: 13, xp: 170, gold: 120, drop: ['玄霜符'] },
    cult: { name: '无相教护法', hp: 270, atk: 45, def: 17, xp: 240, gold: 180, drop: ['星砂'] },
    dragon: { name: '赤蛟残魂', hp: 360, atk: 55, def: 21, xp: 380, gold: 260, drop: ['赤蛟鳞'] },
    lord: { name: '无相教主·谢玄衣', hp: 520, atk: 66, def: 25, xp: 700, gold: 500, drop: ['星砂', '归元丹'] },
  },
  chapters: [
    { place: '青岚镇', title: '雨夜青灯', story: '师父失踪三月，镇外古井忽然反涌桃花。你在药铺后院找到半枚残玉，玉上刻着“无相”二字。镇民请你清掉竹林妖狼，换一条进山的路。', foes: ['wolf', 'wolf'], reward: { gold: 80, items: { 青钢剑: 1 } } },
    { place: '黑风岭', title: '山寨旧账', story: '黑风寨不是普通贼窝，账册里记着给栖霞渡送人的日期。二当家认得师父的剑穗，却只说“活人进了渡口，出来的都是影子”。', foes: ['bandit', 'bandit', 'ghost'], reward: { gold: 120, items: { 兽牙坠: 1, 残玉: 1 } } },
    { place: '栖霞渡', title: '渡口灯影', story: '渡船夜夜空来满去。船娘柳七娘愿意带你过江，条件是取回被青鳞蛇吞下的龙涎香。水面映出师父背影，他似乎在阻止你继续查。', foes: ['snake', 'ghost'], reward: { gold: 180, items: { 霜纹剑: 1, 龙涎香: 1 } } },
    { place: '沉剑关', title: '关城雪', story: '沉剑关军械库被无相教接管，守将留下血书：教众在收集修士心镜，欲开天墟。你救出铁匠韩铮，他替你重铸佩剑。', foes: ['guard', 'guard', 'demon'], reward: { gold: 260, items: { 鳞甲道衣: 1 } } },
    { place: '镜湖', title: '一湖两月', story: '镜湖有两个月亮，一个在天上，一个在水底。水魈用熟人的声音诱你回头；你若斩破倒影，便能看见师父被锁在天墟门前。', foes: ['demon', 'cult'], reward: { gold: 320, items: { 镇魂铃: 1, 星砂: 1 } } },
    { place: '天墟集', title: '市中仙魔', story: '天墟集三百年开一次，仙门、妖市、鬼商同席。你用星砂换到入墟令，也听见谢玄衣的真相：他曾是师父师兄，为复活亡妻不惜以一州心魂炼镜。', foes: ['cult', 'cult', 'dragon'], reward: { gold: 420, items: { 玄铁尺: 1, 归元丹: 1 } } },
    { place: '无相宫', title: '镜碎人间', story: '无相宫悬在破碎星河上。师父尚有一息，愿把毕生修为渡给你。谢玄衣并非癫狂，他清醒地选择牺牲别人。此战之后，青岚镇的雨会停。', foes: ['dragon', 'lord'], reward: { gold: 888, items: { 太乙鹤氅: 1, 星河佩: 1 } } },
  ],
};

const clone = (value) => JSON.parse(JSON.stringify(value));
const addLog = (logs, line) => [line, ...logs].slice(0, 80);

function loadState() {
  try {
    const raw = window.localStorage.getItem(SAVE_KEY);
    return raw ? JSON.parse(raw) : null;
  } catch {
    window.localStorage.removeItem(SAVE_KEY);
    return null;
  }
}

function baseState() {
  return { p: clone(GAME.player), log: ['雨落青岚，故事开始。'], mode: 'town', combat: null };
}

function stats(player) {
  const result = { ...player };
  Object.values(player.equip).forEach((name) => {
    const gear = GAME.gear[name] || {};
    Object.entries(gear).forEach(([key, value]) => {
      if (key !== 'slot') result[key] = (result[key] || 0) + value;
    });
  });
  result.hp = Math.min(result.hp, result.maxHp);
  result.mp = Math.min(result.mp, result.maxMp);
  return result;
}

function addItem(player, name, count = 1) {
  return { ...player, inv: { ...player.inv, [name]: (player.inv[name] || 0) + count } };
}

function gainXp(player, amount) {
  const next = { ...player, xp: player.xp + amount };
  while (next.xp >= next.lvl * 100) {
    next.xp -= next.lvl * 100;
    next.lvl += 1;
    next.maxHp += 28;
    next.maxMp += 12;
    next.atk += 5;
    next.def += 3;
    next.hp = next.maxHp;
    next.mp = next.maxMp;
  }
  return next;
}

function App() {
  const [state, setState] = useState(() => loadState() || baseState());
  const [playerId, setPlayerId] = useState(() => window.localStorage.getItem(PLAYER_KEY) || 'player001');
  const [syncing, setSyncing] = useState(false);
  const player = useMemo(() => stats(state.p), [state.p]);
  const chapter = GAME.chapters[state.p.chapter];
  const knownSkills = GAME.skills.filter((skill) => (skill.lvl || 1) <= state.p.lvl);
  const shop = GAME.shops[chapter?.place] || GAME.shops.青岚镇;

  function update(line, patch = {}) {
    setState((current) => ({ ...current, ...patch, log: addLog(current.log, line) }));
  }

  async function save() {
    window.localStorage.setItem(SAVE_KEY, JSON.stringify(state));
    window.localStorage.setItem(PLAYER_KEY, playerId);
    if (!canUseCloudSave) return update('已保存到本地。配置 VITE_API_BASE 后可同步到 MySQL。');
    setSyncing(true);
    try {
      const response = await fetch(`${API_BASE}/api/saves/${encodeURIComponent(playerId)}`, {
        method: 'PUT',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ save: state }),
      });
      if (!response.ok) throw new Error((await response.json()).error || '云存档失败');
      update('已保存到服务器：MySQL 持久化、Redis 缓存、Kafka 已记录事件。');
    } catch (error) {
      update(`本地已保存，云存档失败：${error.message}`);
    } finally {
      setSyncing(false);
    }
  }

  async function loadCloudSave() {
    if (!canUseCloudSave) return update('还没有配置 VITE_API_BASE，当前只能读取本地存档。');
    setSyncing(true);
    try {
      const response = await fetch(`${API_BASE}/api/saves/${encodeURIComponent(playerId)}`);
      if (!response.ok) throw new Error((await response.json()).error || '读取云存档失败');
      const data = await response.json();
      window.localStorage.setItem(SAVE_KEY, JSON.stringify(data.save));
      window.localStorage.setItem(PLAYER_KEY, playerId);
      setState({ ...data.save, log: addLog(data.save.log || [], `已从${data.source}读取服务器存档。`) });
    } catch (error) {
      update(`读取云存档失败：${error.message}`);
    } finally {
      setSyncing(false);
    }
  }

  function reset() {
    window.localStorage.removeItem(SAVE_KEY);
    setState(baseState());
  }

  function startChapter() {
    if (!chapter) return;
    const foes = chapter.foes.map((id) => ({ ...GAME.mobs[id], id, maxHp: GAME.mobs[id].hp, hp: GAME.mobs[id].hp }));
    update(`踏入【${chapter.title}】。${chapter.story}`, { mode: 'combat', combat: { foes } });
  }

  function damage(attacker, defender, power = 1) {
    return Math.max(1, Math.floor(attacker.atk * power + Math.random() * 8 - defender.def * 0.6));
  }

  function win(combat) {
    let next = state.p;
    let gold = 0;
    const found = [];
    combat.foes.forEach((foe) => {
      next = gainXp(next, foe.xp);
      gold += foe.gold;
      foe.drop.forEach((item) => {
        if (Math.random() < 0.55) {
          next = addItem(next, item);
          found.push(item);
        }
      });
    });
    if (chapter?.reward) {
      gold += chapter.reward.gold;
      Object.entries(chapter.reward.items).forEach(([item, count]) => { next = addItem(next, item, count); });
    }
    next = { ...next, gold: next.gold + gold, chapter: Math.min(next.chapter + 1, GAME.chapters.length) };
    update(`章节完成，得灵石${gold}${found.length ? `，额外获得${found.join('、')}` : ''}。`, { p: next, mode: 'town', combat: null });
  }

  function cast(skill) {
    if (state.mode !== 'combat') return;
    const combat = clone(state.combat);
    let next = { ...state.p };
    const me = stats(next);
    if (me.mp < skill.mp) return update('真元不足，先用回气散或换低消耗招式。');
    next.mp -= skill.mp;
    const foe = combat.foes.find((target) => target.hp > 0);
    const amount = damage(me, foe, skill.power);
    foe.hp -= amount;
    if (skill.healRate) next.hp = Math.min(me.maxHp, next.hp + Math.floor(me.maxHp * skill.healRate));
    let line = `${skill.name}：${skill.text}，对${foe.name}造成${amount}伤害。`;
    if (combat.foes.every((target) => target.hp <= 0)) return win(combat);
    combat.foes.filter((target) => target.hp > 0).forEach((enemy) => {
      const hit = damage(enemy, stats(next));
      next.hp -= hit;
      line += ` ${enemy.name}反击${hit}。`;
    });
    if (next.hp <= 0) {
      next = { ...next, hp: Math.ceil(next.maxHp * 0.6), mp: Math.ceil(next.maxMp * 0.5), gold: Math.max(0, next.gold - 50) };
      return update(`${line} 你力竭昏迷，被柳七娘拖回客栈，损失50灵石。`, { p: next, mode: 'town', combat: null });
    }
    return update(line, { p: next, combat });
  }

  function useItem(name) {
    const item = GAME.items[name];
    if (!state.p.inv[name]) return;
    if (GAME.gear[name]) return equip(name);
    if (!item) return;
    let next = { ...state.p, inv: { ...state.p.inv, [name]: state.p.inv[name] - 1 } };
    if (item.combat && state.mode !== 'combat') return update(`${name}需要在战斗中使用。`);
    if (item.damage) {
      const combat = clone(state.combat);
      const foe = combat.foes.find((target) => target.hp > 0);
      foe.hp -= item.damage;
      update(`${name}击中${foe.name}，造成${item.damage}伤害。`, { p: next, combat });
      if (combat.foes.every((target) => target.hp <= 0)) win(combat);
      return;
    }
    if (item.buffDef) next.def += item.buffDef;
    if (item.full) {
      const full = stats(next);
      next.hp = full.maxHp;
      next.mp = full.maxMp;
    } else {
      const current = stats(next);
      next.hp = Math.min(current.maxHp, next.hp + (item.hp || 0));
      next.mp = Math.min(current.maxMp, next.mp + (item.mp || 0));
    }
    update(`使用${name}。`, { p: next });
  }

  function buy(name, price) {
    if (state.p.gold < price) return update('灵石不足。');
    update(`买下${name}。`, { p: addItem({ ...state.p, gold: state.p.gold - price }, name) });
  }

  function equip(name) {
    const gear = GAME.gear[name];
    if (!gear) return;
    update(`装备${name}。`, { p: { ...state.p, equip: { ...state.p.equip, [gear.slot]: name } } });
  }

  return (
    <div className="game-shell">
      <header>
        <h1>青岚无相录</h1>
        <label className="player-id">玩家ID<input value={playerId} onChange={(event) => setPlayerId(event.target.value)} /></label>
        <button onClick={save} disabled={syncing}><Save />{syncing ? '同步中' : '保存'}</button>
        <button onClick={loadCloudSave} disabled={syncing}>读档</button>
        <button onClick={reset}><RotateCcw />重开</button>
      </header>
      <main>
        <section className="card hero">
          <h2>{state.p.name} Lv.{state.p.lvl}</h2>
          <p>气血 {player.hp}/{player.maxHp}　真元 {player.mp}/{player.maxMp}　灵石 {state.p.gold}　阅历 {state.p.xp}/{state.p.lvl * 100}</p>
          <p>攻 {player.atk} 防 {player.def}　地点：{chapter?.place || '青岚镇'}</p>
          <p>兵器 {state.p.equip.weapon}｜衣甲 {state.p.equip.armor}｜佩饰 {state.p.equip.trinket}</p>
        </section>
        <section className="card story">
          <h2><ScrollText /> {chapter ? chapter.title : '尾声：雨停青岚'}</h2>
          <p>{chapter ? chapter.story : '谢玄衣伏诛，师父在晨光中散去。你回到镇上开了一间小小剑馆，偶尔也会在雨夜想起天墟星河。江湖仍有妖邪，但人间不再是别人的祭品。'}</p>
          {chapter && state.mode !== 'combat' && <button className="primary" onClick={startChapter}><Map /> 前往下一段</button>}
        </section>
        {state.mode === 'combat' && (
          <section className="card combat">
            <h2><Swords /> 回合战斗</h2>
            <div className="foes">{state.combat.foes.map((foe, index) => <div className="foe" key={`${foe.id}-${index}`}>{foe.name}<b>{Math.max(0, foe.hp)}/{foe.maxHp}</b><meter max={foe.maxHp} value={Math.max(0, foe.hp)} /></div>)}</div>
            <div>{knownSkills.map((skill) => <button onClick={() => cast(skill)} key={skill.name}><Flame /> {skill.name}({skill.mp})</button>)}</div>
          </section>
        )}
        <section className="card">
          <h2><Package /> 行囊</h2>
          <div className="grid">{Object.entries(state.p.inv).filter(([, count]) => count > 0).map(([name, count]) => <button key={name} onClick={() => useItem(name)} onDoubleClick={() => equip(name)}>{name} ×{count}<small>{GAME.items[name]?.desc || '装备：点击或双击穿戴'}</small></button>)}</div>
        </section>
        <section className="card"><h2>市集</h2>{shop.map(([name, price]) => <button onClick={() => buy(name, price)} key={name}>买 {name} {price}灵石</button>)}</section>
        <section className="card log"><h2>江湖札记</h2>{state.log.map((line, index) => <p key={`${index}-${line}`}>{line}</p>)}</section>
      </main>
      <footer>提示：单击药品/符箓使用，单击或双击装备穿戴。战败不删档，可以刷掉落、升级后再推进。</footer>
    </div>
  );
}

createRoot(document.getElementById('root')).render(<App />);
