// Auth service — API calls for authentication
window.AuthService = {
  baseUrl: '/api/auth',
  storage: window.AuthStorage,

  // Single in-flight refresh promise. Refresh tokens rotate on the server, so two
  // concurrent /refresh calls with the same token make one win and the other 401 —
  // which used to log the user out. We dedupe: all callers share one request.
  _refreshPromise: null,

  // Request magic link
  async requestLoginLink(email) {
    try {
      const res = await fetch(`${this.baseUrl}/login`, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ Email: email })
      });
      if (!res.ok) {
        const err = await res.json();
        throw new Error(err.error || 'Failed to send magic link');
      }
      return { success: true };
    } catch (err) {
      console.error('requestLoginLink error:', err);
      throw err;
    }
  },

  // Verify magic link token
  async verifyLogin(token) {
    try {
      const res = await fetch(`${this.baseUrl}/verify`, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ Token: token })
      });
      if (!res.ok) {
        const err = await res.json();
        throw new Error(err.error || 'Failed to verify login');
      }
      const data = await res.json();
      this._setAuthAndNotify(data);
      return data;
    } catch (err) {
      console.error('verifyLogin error:', err);
      throw err;
    }
  },

  // Google sign-in
  async googleSignIn(idToken) {
    try {
      const res = await fetch(`${this.baseUrl}/google`, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ IdToken: idToken })
      });
      if (!res.ok) {
        const err = await res.json();
        throw new Error(err.error || 'Google sign-in failed');
      }
      const data = await res.json();
      this._setAuthAndNotify(data);
      return data;
    } catch (err) {
      console.error('googleSignIn error:', err);
      throw err;
    }
  },

  // Apple sign-in
  async appleSignIn(identityToken) {
    try {
      const res = await fetch(`${this.baseUrl}/apple`, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ IdentityToken: identityToken })
      });
      if (!res.ok) {
        const err = await res.json();
        throw new Error(err.error || 'Apple sign-in failed');
      }
      const data = await res.json();
      this._setAuthAndNotify(data);
      return data;
    } catch (err) {
      console.error('appleSignIn error:', err);
      throw err;
    }
  },

  // Refresh access token — single-flight wrapper.
  // Concurrent callers (periodic check, ensureValidToken, 401 retry, init) all await
  // the same in-flight request instead of firing duplicate /refresh calls.
  async refreshToken() {
    if (this._refreshPromise) {
      console.log('[AuthService] Refresh already in progress, reusing in-flight request');
      return this._refreshPromise;
    }

    this._refreshPromise = this._doRefresh();
    try {
      return await this._refreshPromise;
    } finally {
      this._refreshPromise = null;
    }
  },

  // Actual refresh request. Do not call directly — go through refreshToken().
  async _doRefresh() {
    const refreshToken = this.storage.getRefreshToken();
    console.log('[AuthService] Attempting refresh, has refreshToken:', !!refreshToken);

    if (!refreshToken) {
      console.error('[AuthService] No refresh token available');
      this.storage.clearAuth();
      throw new Error('No refresh token available - session lost');
    }

    try {
      const res = await fetch(`${this.baseUrl}/refresh`, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ RefreshToken: refreshToken })
      });

      console.log('[AuthService] Refresh response status:', res.status);

      if (!res.ok) {
        const errText = await res.text();
        console.error('[AuthService] Refresh failed:', res.status, errText);

        if (res.status === 401 || res.status === 403) {
          console.log('[AuthService] Refresh token invalid, clearing auth');
          this.storage.clearAuth();
          throw new Error('Refresh token expired - please log in again');
        }

        throw new Error(`Refresh failed: ${res.status}`);
      }

      const data = await res.json();
      console.log('[AuthService] Refresh response received:', {
        hasAccessToken: !!data.accessToken,
        hasRefreshToken: !!data.refreshToken,
        expiresAt: data.expiresAt,
        fullResponse: JSON.stringify(data).substring(0, 200)
      });

      if (!data.accessToken || !data.refreshToken || !data.expiresAt) {
        console.error('[AuthService] ✗ Invalid refresh response, missing fields:', {
          accessToken: !!data.accessToken,
          refreshToken: !!data.refreshToken,
          expiresAt: data.expiresAt
        });
        this.storage.clearAuth();
        throw new Error('Invalid refresh response from server');
      }

      console.log('[AuthService] ✓ Refresh response valid, updating storage...');
      this._setAuthAndNotify(data);
      console.log('[AuthService] ✓ Auth updated successfully');
      return data;
    } catch (err) {
      console.error('[AuthService] refreshToken error:', err);
      if (err.message.includes('expired') || err.message.includes('invalid')) {
        this.storage.clearAuth();
      }
      throw err;
    }
  },

  // Logout
  async logout() {
    try {
      const refreshToken = this.storage.getRefreshToken();
      if (refreshToken) {
        console.log('[AuthService] Logging out...');
        await fetch(`${this.baseUrl}/logout`, {
          method: 'POST',
          headers: { 'Content-Type': 'application/json' },
          body: JSON.stringify({ RefreshToken: refreshToken })
        });
      }
    } catch (err) {
      console.error('[AuthService] logout error:', err);
    } finally {
      this.storage.clearAuth();
      console.log('[AuthService] Auth cleared');
    }
  },

  // Internal helper to set auth and trigger storage events
  _setAuthAndNotify(data) {
    console.log('[AuthService] _setAuthAndNotify called with:', {
      accessToken: data.accessToken ? data.accessToken.substring(0, 20) + '...' : 'MISSING',
      refreshToken: data.refreshToken ? data.refreshToken.substring(0, 20) + '...' : 'MISSING',
      expiresAt: data.expiresAt,
      expiresAtType: typeof data.expiresAt
    });

    try {
      this.storage.setAuth(data.accessToken, data.refreshToken, data.expiresAt);
      console.log('[AuthService] ✓ setAuth completed successfully');
    } catch (err) {
      console.error('[AuthService] ✗ setAuth FAILED:', err);
      throw err;
    }

    // Also dispatch storage event for other tabs
    try {
      window.dispatchEvent(new StorageEvent('storage', {
        key: 'sleepagotchi_auth',
        newValue: JSON.stringify({ accessToken: data.accessToken, refreshToken: data.refreshToken, expiresAt: data.expiresAt })
      }));
      console.log('[AuthService] ✓ Storage event dispatched');
    } catch (err) {
      console.error('[AuthService] ⚠ Storage event dispatch failed:', err);
    }
  },

  // Check and refresh token if needed
  async ensureValidToken() {
    const auth = this.storage.getAuth();
    if (!auth) {
      throw new Error('No auth data in storage');
    }

    const isExpired = this.storage.isTokenExpired();
    console.log('[AuthService] ensureValidToken - token expired:', isExpired);

    if (isExpired) {
      console.log('[AuthService] Token expired, refreshing...');
      return await this.refreshToken();
    }

    console.log('[AuthService] Token still valid');
    return auth;
  },

  // Get auth header for API calls
  getAuthHeader() {
    const token = this.storage.getAccessToken();
    if (!token) return {};
    return { 'Authorization': `Bearer ${token}` };
  },

  // Check if user is authenticated
  isAuthenticated() {
    return !this.storage.isTokenExpired() && !!this.storage.getAccessToken();
  },

  // Make authenticated API call with automatic token refresh
  async fetchWithAuth(url, options = {}) {
    try {
      await this.ensureValidToken();
    } catch (err) {
      console.error('[AuthService] Token not valid before API call:', err);
      throw new Error('Session invalid - please log in again');
    }

    const headers = {
      ...options.headers,
      ...this.getAuthHeader(),
    };

    let response = await fetch(url, {
      ...options,
      headers,
    });

    if (response.status === 401) {
      console.log('[AuthService] Got 401, attempting token refresh and retry...');
      try {
        await this.refreshToken();
        const retryHeaders = {
          ...options.headers,
          ...this.getAuthHeader(),
        };
        response = await fetch(url, {
          ...options,
          headers: retryHeaders,
        });
      } catch (err) {
        console.error('[AuthService] Failed to recover from 401:', err);
        throw new Error('Session expired - please log in again');
      }
    }

    return response;
  }
};

window.AuthService.storage;
