/**
 * VBase - Scoped RBAC 权限管理客户端
 * 对应后端: github.com/veypi/vigo/contrib/auth.Auth 接口
 */

// 权限等级常量 (与后端一致)
export const Level = {
  None: 0,
  Create: 1,      // 001 创建 (奇数层)
  Read: 2,        // 010 读取 (偶数层)
  Write: 4,       // 100 写入 (偶数层)
  ReadWrite: 6,   // 110 读写 (偶数层)
  Admin: 7,       // 111 管理员 (完全控制)
};

class VBase {
  constructor(scope, baseURL, login_page) {
    if (!scope) throw new Error('VBase: scope is required');
    if (!baseURL) baseURL = window.location.origin;
    if (baseURL === '' || baseURL === '/') baseURL = window.location.origin;

    this.baseURL = baseURL;
    this.scope = scope;
    this.login_page = login_page || (baseURL + '/login')
    this.tokenKey = `vbase_token`;
    this.refreshTokenKey = `vbase_refresh_token`;
    this.userKey = `vbase_user`;

    this._token = localStorage.getItem(this.tokenKey) || '';
    this._refreshToken = localStorage.getItem(this.refreshTokenKey) || '';
    this._user = JSON.parse(localStorage.getItem(this.userKey) || 'null');
  }

  // ========== Getters ==========
  get token() { return this._token; }
  get refreshToken() { return this._refreshToken; }
  get user() { return this._user; }

  // ========== Setters ==========
  set token(val) {
    this._token = val;
    if (val) localStorage.setItem(this.tokenKey, val);
    else localStorage.removeItem(this.tokenKey);
  }

  set refreshToken(val) {
    this._refreshToken = val;
    if (val) localStorage.setItem(this.refreshTokenKey, val);
    else localStorage.removeItem(this.refreshTokenKey);
  }

  set user(val) {
    this._user = val;
    if (val) localStorage.setItem(this.userKey, JSON.stringify(val));
    else localStorage.removeItem(this.userKey);
  }

  // ========== API 请求 ==========
  async request(method, path, data = null, headers = {}) {
    const url = `${this.baseURL}${path}`;
    const config = {
      method,
      headers: {
        'Content-Type': 'application/json',
        ...this.getAuthHeaders(),
        ...headers
      }
    };
    if (data) config.body = JSON.stringify(data);

    const response = await fetch(url, config);
    const resData = await response.json();

    if (!response.ok) {
      const error = new Error(resData.message || `Request failed: ${response.status}`);
      Object.assign(error, resData);
      throw error;
    }

    if (resData.code && resData.code !== 200) {
      throw new Error(resData.message || 'API Error');
    }

    return resData.data || resData;
  }

  // ========== 认证相关 ==========
  async login(username, password) {
    const data = await this.request('POST', '/api/auth/login', { username, password });
    if (data.access_token) {
      this.token = data.access_token;
      if (data.refresh_token) this.refreshToken = data.refresh_token;
      this.user = data.user;
      return true;
    }
    return false;
  }

  /**
   * OAuth Callback Handler
   * @param {string} provider
   * @param {string} code
   * @param {string} state
   * @returns {Promise<Object>} Response data
   */
  async oauthCallback(provider, code, state) {
    const data = await this.request('GET', `/api/auth/callback/${provider}?code=${code}&state=${state}`);

    // If login success directly
    if (data.access_token || data.token) {
      this.token = data.access_token || data.token;
      if (data.refresh_token) this.refreshToken = data.refresh_token;
      if (data.user) this.user = data.user;
    }

    return data;
  }

  /**
   * Bind existing account
   * @param {string} tempToken 
   * @param {string} username 
   * @param {string} password 
   * @returns {Promise<Object>}
   */
  async bindAccount(tempToken, username, password) {
    const data = await this.request('POST', '/api/auth/bind', {
      temp_token: tempToken,
      username,
      password
    });

    if (data.access_token || data.token) {
      this.token = data.access_token || data.token;
      if (data.refresh_token) this.refreshToken = data.refresh_token;
      if (data.user) this.user = data.user;
    }

    return data;
  }

  /**
   * Register and bind new account
   * @param {string} tempToken 
   * @param {string} username 
   * @param {string} email 
   * @returns {Promise<Object>}
   */
  async bindRegister(tempToken, username, email) {
    const data = await this.request('POST', '/api/auth/bind-register', {
      temp_token: tempToken,
      username,
      email
    });

    if (data.access_token || data.token) {
      this.token = data.access_token || data.token;
      if (data.refresh_token) this.refreshToken = data.refresh_token;
      if (data.user) this.user = data.user;
    }

    return data;
  }

  async logout(redirect) {
    try {
      await this.request('POST', '/api/auth/logout');
    } catch (e) {
      console.warn('Logout API failed', e);
    } finally {
      this.clear();
      const redirectUrl = redirect || window.location.pathname + window.location.search;
      location.href = this.login_page + '?redirect=' + encodeURIComponent(redirectUrl);
    }
  }

  async refresh() {
    if (!this.refreshToken) throw new Error("No refresh token");
    try {
      const data = await this.request('POST', '/api/auth/refresh', { refresh_token: this.refreshToken });
      if (data.access_token) {
        this.token = data.access_token;
        if (data.refresh_token) this.refreshToken = data.refresh_token;
        return true;
      }
      return false;
    } catch (e) {
      this.logout();
      throw e;
    }
  }

  async fetchUser() {
    const user = await this.request('GET', '/api/auth/me');
    this.user = user;
    return user;
  }

  getAuthHeaders() {
    const headers = {};
    if (this.token) headers['Authorization'] = `Bearer ${this.token}`;
    return headers;
  }

  clear() {
    this.token = '';
    this.refreshToken = '';
    this.user = null;
  }

  isExpired(token) {
    if (!token) token = this.token;
    if (!token) return true;
    try {
      const base64Url = token.split('.')[1];
      const base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/');
      const payload = JSON.parse(window.atob(base64));
      const now = Math.floor(Date.now() / 1000);
      return payload.exp && payload.exp < now;
    } catch (e) {
      return true;
    }
  }

  // ========== 权限检查 (与后端接口一致) ==========

  /**
   * 检查权限
   * @param {string} code - 权限码，如 "resource:instance"
   * @param {number} level - 需要的权限等级
   * @returns {boolean}
   */
  Perm(code, level = Level.Read) {
    if (!this.user) return false;
    if (!code) return true;

    const perms = this.user.permissions || [];
    return this._checkPermissionLevel(perms, code, level);
  }

  /**
   * 检查创建权限 (level 1，检查奇数层)
   * @param {string} code - 权限码
   * @returns {boolean}
   */
  PermCreate(code) {
    return this.Perm(code, Level.Create);
  }

  /**
   * 检查读取权限 (level 2，检查偶数层)
   * @param {string} code - 权限码
   * @returns {boolean}
   */
  PermRead(code) {
    return this.Perm(code, Level.Read);
  }

  /**
   * 检查更新权限 (level 4，检查偶数层)
   * @param {string} code - 权限码
   * @returns {boolean}
   */
  PermWrite(code) {
    return this.Perm(code, Level.Write);
  }

  /**
   * 检查管理员权限 (level 7，检查偶数层)
   * @param {string} code - 权限码
   * @returns {boolean}
   */
  PermAdmin(code) {
    return this.Perm(code, Level.Admin);
  }

  // ========== 内部方法 ==========

  /**
   * 核心权限检查逻辑 (与后端 checkPermissionLevel 一致)
   */
  _checkPermissionLevel(perms, targetPermID, requiredLevel) {
    for (const p of perms) {
      const permID = p.permission_id || p;
      const permLevel = p.level !== undefined ? p.level : Level.Read;

      // 1. 管理员特权 (Level 7 且是父级或同级)
      if (permLevel === Level.Admin) {
        if (permID === '*' || targetPermID.startsWith(permID + ':') || permID === targetPermID) {
          return true;
        }
      }

      // 2. 普通权限匹配
      if (permLevel >= requiredLevel) {
        if (permID === targetPermID) {
          return true;
        }
      }
    }

    return false;
  }

  // ========== Axios 集成 ==========

  wrapAxios(axiosInstance) {
    // 请求拦截器
    axiosInstance.interceptors.request.use(config => {
      const headers = this.getAuthHeaders();
      for (const key in headers) {
        config.headers[key] = headers[key];
      }
      return config;
    }, error => Promise.reject(error));

    // 响应拦截器
    axiosInstance.interceptors.response.use(
      response => response,
      async error => {
        const originalRequest = error.config;
        if (error.response?.status === 401 && !originalRequest._retry) {
          originalRequest._retry = true;
          try {
            await this.refresh();
            const headers = this.getAuthHeaders();
            originalRequest.headers['Authorization'] = headers['Authorization'];
            return axiosInstance(originalRequest);
          } catch (e) {
            this.logout();
            return Promise.reject(e);
          }
        }
        return Promise.reject(error);
      }
    );
  }
}

export default VBase;
