'use client';

import { create } from 'zustand';
import { persist, createJSONStorage } from 'zustand/middleware';
import type { AccessTokenPayload } from '@pac/types';

interface AuthState {
  accessToken: string | null;
  refreshToken: string | null;
  expiresAt: number | null;
  user: AccessTokenPayload | null;
  setTokens: (input: { accessToken: string; refreshToken: string; expiresIn: number }) => void;
  clear: () => void;
  isAuthenticated: () => boolean;
  /** refresh token 还在(可能 access 过期),允许 silent refresh */
  canRefresh: () => boolean;
}

function decodeJwt(token: string): AccessTokenPayload | null {
  try {
    const [, payload] = token.split('.');
    if (!payload) return null;
    // atob 解出的是二进制字符串(每字符 = 1 字节);直接 JSON.parse 会把多字节
    // UTF-8(如中文诊所名 dictionary)按 Latin-1 误读成乱码,必须按 UTF-8 重新解码。
    const binary = atob(payload.replace(/-/g, '+').replace(/_/g, '/'));
    const bytes = Uint8Array.from(binary, (c) => c.charCodeAt(0));
    const json = new TextDecoder('utf-8').decode(bytes);
    return JSON.parse(json) as AccessTokenPayload;
  } catch {
    return null;
  }
}

/**
 * Auth state — 持久化到 localStorage(per-origin)。
 *
 * 解决:iframe / 新标签 F5 后丢 session 的问题。
 *   - URL 上的 ?code= 一次性消费完会被剥掉,F5 后 URL 没 code → 之前内存态会死
 *   - 现在:localStorage 兜底,F5 后从 store 读 token,api-client 自动 silent refresh
 *
 * 安全考量:
 *   - accessToken 2h,泄漏窗口短
 *   - refreshToken 7d,后端有 rotation(刷新即作废老的),被偷会留痕
 *   - XSS 风险:Next + Tailwind 默认无 innerHTML 注入路径,业务上避免 dangerouslySetInnerHTML
 *   - 医疗 SaaS 行业标准做法
 *
 * Safari ITP 注:iframe 第三方 localStorage 可能 7 天没活跃被清。
 * 兜底由调用方处理:store 空 → URL 看有没 code → 都没有 → 引导用户回 host 重开。
 */
export const useAuthStore = create<AuthState>()(
  persist(
    (set, get) => ({
      accessToken: null,
      refreshToken: null,
      expiresAt: null,
      user: null,
      setTokens: ({ accessToken, refreshToken, expiresIn }) => {
        set({
          accessToken,
          refreshToken,
          expiresAt: Date.now() + expiresIn * 1000,
          user: decodeJwt(accessToken),
        });
      },
      clear: () => set({ accessToken: null, refreshToken: null, expiresAt: null, user: null }),
      isAuthenticated: () => {
        const { accessToken, expiresAt } = get();
        return Boolean(accessToken && expiresAt && Date.now() < expiresAt);
      },
      canRefresh: () => Boolean(get().refreshToken),
    }),
    {
      name: 'pac-auth',
      storage: createJSONStorage(() => localStorage),
      // 只持久化原始 token,派生字段(user)反水合时从 token 重新 decode
      partialize: (s) => ({
        accessToken: s.accessToken,
        refreshToken: s.refreshToken,
        expiresAt: s.expiresAt,
      }),
      onRehydrateStorage: () => (state) => {
        if (state?.accessToken) {
          state.user = decodeJwt(state.accessToken);
        }
      },
    },
  ),
);
