import apiClient from '../utils/api';
import type {
  SpotifyAuthResponse,
  SpotifyPlaylistsResponse,
  SpotifyBackendPlaylistsResponse,
  SpotifyAddTracksRequest,
  SpotifyAddTracksResponse,
  SpotifyAuthCallbackRequest,
  SpotifyAuthCallbackResponse,
  ApiResponse,
} from '../types/api';

/**
 * Spotify OAuth 2.0 서비스 (Session 기반)
 */
class SpotifyService {
  private sessionIdKey = 'spotify_session_id';
  private sessionExpiresAtKey = 'spotify_session_expires_at';

  // Spotify OAuth 설정
  private clientId = import.meta.env.VITE_SPOTIFY_CLIENT_ID || '';
  private redirectUri = this.getRedirectUri();
  private scopes = [
    'playlist-read-private',
    'playlist-read-collaborative',
    'playlist-modify-public',
    'playlist-modify-private',
  ];

  /**
   * Redirect URI 생성
   * Spotify는 localhost를 지원하지 않으므로 127.0.0.1로 변환
   */
  private getRedirectUri(): string {
    if (import.meta.env.VITE_SPOTIFY_REDIRECT_URI) {
      return import.meta.env.VITE_SPOTIFY_REDIRECT_URI;
    }

    // window.location.origin에서 localhost를 127.0.0.1로 변환
    let origin = window.location.origin;
    origin = origin.replace('localhost', '127.0.0.1');

    return `${origin}/callback/spotify`;
  }

  /**
   * OAuth 로그인 팝업 열기
   */
  async login(): Promise<SpotifyAuthResponse | null> {
    // Client ID 검증
    if (!this.clientId || this.clientId === 'your_spotify_client_id_here') {
      throw new Error('Spotify Client ID가 설정되지 않았습니다. .env 파일에서 VITE_SPOTIFY_CLIENT_ID를 설정해주세요.');
    }

    const state = this.generateRandomString(16);
    const codeVerifier = this.generateRandomString(128);
    const codeChallenge = await this.generateCodeChallenge(codeVerifier);

    // PKCE 파라미터 저장
    sessionStorage.setItem('spotify_code_verifier', codeVerifier);
    sessionStorage.setItem('spotify_state', state);

    const authUrl = new URL('https://accounts.spotify.com/authorize');
    authUrl.searchParams.append('client_id', this.clientId);
    authUrl.searchParams.append('response_type', 'code');
    authUrl.searchParams.append('redirect_uri', this.redirectUri);
    authUrl.searchParams.append('state', state);
    authUrl.searchParams.append('scope', this.scopes.join(' '));
    authUrl.searchParams.append('code_challenge_method', 'S256');
    authUrl.searchParams.append('code_challenge', codeChallenge);

    return new Promise((resolve, reject) => {
      const width = 500;
      const height = 700;
      const left = window.screenX + (window.outerWidth - width) / 2;
      const top = window.screenY + (window.outerHeight - height) / 2;

      const popup = window.open(
        authUrl.toString(),
        'Spotify Login',
        `width=${width},height=${height},left=${left},top=${top}`
      );

      if (!popup) {
        reject(new Error('팝업이 차단되었습니다. 팝업 차단을 해제해주세요.'));
        return;
      }

      // 팝업에서 메시지 수신 대기
      const messageHandler = async (event: MessageEvent) => {
        if (event.origin !== window.location.origin) return;

        if (event.data.type === 'spotify_auth_success') {
          window.removeEventListener('message', messageHandler);
          popup.close();

          const { code, state: returnedState } = event.data;
          const savedState = sessionStorage.getItem('spotify_state');

          if (returnedState !== savedState) {
            reject(new Error('State 값이 일치하지 않습니다.'));
            return;
          }

          try {
            const tokenResponse = await this.exchangeCodeForToken(code);
            resolve(tokenResponse);
          } catch (error) {
            reject(error);
          }
        } else if (event.data.type === 'spotify_auth_error') {
          window.removeEventListener('message', messageHandler);
          popup.close();
          reject(new Error(event.data.error || 'OAuth 인증에 실패했습니다.'));
        }
      };

      window.addEventListener('message', messageHandler);

      // 팝업이 닫혔는지 확인 (COOP 에러 방지)
      const checkClosed = setInterval(() => {
        try {
          if (popup.closed) {
            clearInterval(checkClosed);
            window.removeEventListener('message', messageHandler);
            reject(new Error('로그인이 취소되었습니다.'));
          }
        } catch (error) {
          // Cross-Origin-Opener-Policy 에러 무시
          console.debug('Popup 상태 확인 중 COOP 에러 발생 (정상)');
        }
      }, 1000);
    });
  }

  /**
   * Authorization code를 access token으로 교환
   */
  private async exchangeCodeForToken(code: string): Promise<SpotifyAuthResponse> {
    const codeVerifier = sessionStorage.getItem('spotify_code_verifier');

    if (!codeVerifier) {
      throw new Error('Code verifier를 찾을 수 없습니다.');
    }

    const body = new URLSearchParams({
      grant_type: 'authorization_code',
      code,
      redirect_uri: this.redirectUri,
      client_id: this.clientId,
      code_verifier: codeVerifier,
    });

    const response = await fetch('https://accounts.spotify.com/api/token', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/x-www-form-urlencoded',
      },
      body: body.toString(),
    });

    if (!response.ok) {
      const error = await response.json();
      throw new Error(error.error_description || 'Token 교환에 실패했습니다.');
    }

    const data: SpotifyAuthResponse = await response.json();

    // 세션 스토리지 정리
    sessionStorage.removeItem('spotify_code_verifier');
    sessionStorage.removeItem('spotify_state');

    return data;
  }

  /**
   * OAuth 토큰을 백엔드로 전송하여 세션 생성
   */
  async sendAuthCallback(authData: SpotifyAuthResponse): Promise<ApiResponse<SpotifyAuthCallbackResponse>> {
    const request: SpotifyAuthCallbackRequest = {
      access_token: authData.access_token,
      token_type: authData.token_type,
      expires_in: authData.expires_in,
      refresh_token: authData.refresh_token,
      scope: authData.scope,
    };

    const response = await apiClient.post<SpotifyAuthCallbackResponse>(
      '/v2/spotify/auth/callback',
      request
    );

    if (response.success && response.data) {
      this.setSessionId(response.data.session_id);
      this.setSessionExpiresAt(Date.now() + response.data.expires_in * 1000);
    }

    return response;
  }

  /**
   * 로그아웃
   */
  logout(): void {
    this.removeSessionId();
    this.removeSessionExpiresAt();
  }

  /**
   * 백엔드 API를 통해 플레이리스트 목록 가져오기
   */
  async getPlaylistsFromBackend(): Promise<ApiResponse<SpotifyBackendPlaylistsResponse>> {
    const sessionId = this.getSessionId();

    if (!sessionId) {
      return {
        success: false,
        error: '로그인이 필요합니다.',
      };
    }

    return await apiClient.get<SpotifyBackendPlaylistsResponse>('/v2/spotify/playlists', {
      headers: {
        'X-Session-Id': sessionId,
      },
    });
  }

  /**
   * 백엔드 API를 통해 트랙 추가
   */
  async addTracksToPlaylist(request: SpotifyAddTracksRequest): Promise<ApiResponse<SpotifyAddTracksResponse>> {
    const sessionId = this.getSessionId();

    if (!sessionId) {
      return {
        success: false,
        error: '로그인이 필요합니다.',
      };
    }

    return await apiClient.post<SpotifyAddTracksResponse>('/v2/spotify/playlists/tracks', request, {
      headers: {
        'X-Session-Id': sessionId,
      },
    });
  }

  /**
   * 로그인 상태 확인
   */
  isLoggedIn(): boolean {
    const sessionId = this.getSessionId();
    const expiresAt = this.getSessionExpiresAt();

    if (!sessionId || !expiresAt) return false;

    return Date.now() < expiresAt;
  }

  // Session 관리 메서드
  private setSessionId(sessionId: string): void {
    localStorage.setItem(this.sessionIdKey, sessionId);
  }

  getSessionId(): string | null {
    return localStorage.getItem(this.sessionIdKey);
  }

  private removeSessionId(): void {
    localStorage.removeItem(this.sessionIdKey);
  }

  private setSessionExpiresAt(timestamp: number): void {
    localStorage.setItem(this.sessionExpiresAtKey, timestamp.toString());
  }

  private getSessionExpiresAt(): number | null {
    const value = localStorage.getItem(this.sessionExpiresAtKey);
    return value ? parseInt(value, 10) : null;
  }

  private removeSessionExpiresAt(): void {
    localStorage.removeItem(this.sessionExpiresAtKey);
  }

  // PKCE 유틸리티 메서드
  private generateRandomString(length: number): string {
    const possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
    const values = crypto.getRandomValues(new Uint8Array(length));
    return values.reduce((acc, x) => acc + possible[x % possible.length], '');
  }

  private async generateCodeChallenge(codeVerifier: string): Promise<string> {
    const data = new TextEncoder().encode(codeVerifier);
    const digest = await crypto.subtle.digest('SHA-256', data);
    return btoa(String.fromCharCode(...new Uint8Array(digest)))
      .replace(/\+/g, '-')
      .replace(/\//g, '_')
      .replace(/=+$/, '');
  }
}

// 싱글톤 인스턴스 생성 및 export
const spotifyService = new SpotifyService();
export default spotifyService;
