import { apiClient } from '@/services/api/client';
import { AxiosError } from 'axios';
import { logger } from '@/utils/logger';

/**
 * FE-PAGE-015: Analytics service for fetching track and playlist statistics
 * FE-API-010: Analytics service integration
 */

export interface TrackAnalytics {
  total_tracks: number;
  total_plays: number;
  total_likes: number;
  total_downloads: number;
  average_play_count: number;
  top_tracks: Array<{
    id: string;
    title: string;
    play_count: number;
    like_count: number;
  }>;
}

export interface PlaylistAnalytics {
  total_playlists: number;
  total_plays: number;
  total_likes: number;
  total_shares: number;
  average_play_count: number;
  top_playlists: Array<{
    id: string;
    name: string;
    play_count: number;
    like_count: number;
  }>;
}

export interface AnalyticsData {
  tracks: TrackAnalytics;
  playlists: PlaylistAnalytics;
  period: {
    start_date: string;
    end_date: string;
    days: number;
  };
}

export interface TrackStats {
  total_plays: number;
  unique_listeners: number;
  average_duration: number;
  completion_rate: number;
  views?: number;
  likes?: number;
  comments?: number;
  total_play_time?: number;
  downloads?: number;
}

export interface UserStats {
  total_plays: number;
  unique_tracks: number;
  total_duration: number;
  average_duration: number;
  user_id?: string;
  followers_count?: number;
  following_count?: number;
  tracks_count?: number;
  playlists_count?: number;
  likes_count?: number;
  comments_count?: number;
}

interface TrackWithStats {
  id: string;
  title: string;
  play_count?: number;
  like_count?: number;
  download_count?: number;
}

interface PlaylistWithStats {
  id: string;
  name: string;
  play_count?: number;
  like_count?: number;
  share_count?: number;
}

/**
 * Fetch analytics data for tracks and playlists
 * @param days Number of days to fetch analytics for (default: 30)
 * @param startDate Optional start date (ISO string)
 * @param endDate Optional end date (ISO string)
 * @returns Analytics data for the specified period
 * @throws Error if the request fails and fallback also fails
 */
export async function getAnalyticsData(
  days: number = 30,
  startDate?: string,
  endDate?: string,
): Promise<AnalyticsData> {
  try {
    const params: Record<string, string> = { days: days.toString() };
    if (startDate) params.start_date = startDate;
    if (endDate) params.end_date = endDate;

    // Try to get analytics from backend
    const response = await apiClient.get<{ data: AnalyticsData }>(
      '/analytics',
      {
        params,
      },
    );

    if (response.data?.data) {
      return response.data.data;
    }

    // Fallback: aggregate data from user's tracks and playlists
    return await getAggregatedAnalytics(days, startDate, endDate);
  } catch (error) {
    logger.error('Failed to fetch analytics data', {
      error: error instanceof Error ? error.message : String(error),
      stack: error instanceof Error ? error.stack : undefined,
      days,
    });

    // If it's a network error, server error, or 404 (endpoint not implemented), try fallback
    if (
      error instanceof AxiosError &&
      (error.code === 'ECONNABORTED' ||
        error.code === 'ETIMEDOUT' ||
        !error.response ||
        error.response.status >= 500 ||
        error.response.status === 404) // Endpoint not implemented yet
    ) {
      try {
        return await getAggregatedAnalytics(days, startDate, endDate);
      } catch (fallbackError) {
        logger.error('Fallback analytics aggregation also failed', {
          error:
            fallbackError instanceof Error
              ? fallbackError.message
              : String(fallbackError),
          stack:
            fallbackError instanceof Error ? fallbackError.stack : undefined,
          days,
        });
        // Return default analytics on error
        return getDefaultAnalytics(days, startDate, endDate);
      }
    }

    // For other errors (400, 401, 403), throw the error
    if (error instanceof AxiosError) {
      throw new Error(
        error.response?.data?.error ||
          error.message ||
          'Failed to fetch analytics data',
      );
    }

    // Return default analytics for unknown errors
    return getDefaultAnalytics(days, startDate, endDate);
  }
}

/**
 * Aggregate analytics from user's tracks and playlists
 * @param days Number of days
 * @param startDate Optional start date
 * @param endDate Optional end date
 * @returns Aggregated analytics data
 */
async function getAggregatedAnalytics(
  days: number,
  startDate?: string,
  endDate?: string,
): Promise<AnalyticsData> {
  try {
    // Get user's tracks
    const tracksResponse = await apiClient.get('/api/v1/tracks', {
      params: { limit: 1000 },
    });

    // Get user's playlists
    const playlistsResponse = await apiClient.get('/api/v1/playlists', {
      params: { limit: 1000 },
    });

    const tracks = tracksResponse.data?.data?.tracks || [];
    const playlists = playlistsResponse.data?.data?.playlists || [];

    // Calculate track analytics
    const totalPlays = tracks.reduce(
      (sum: number, track: TrackWithStats) => sum + (track.play_count || 0),
      0,
    );
    const totalLikes = tracks.reduce(
      (sum: number, track: TrackWithStats) => sum + (track.like_count || 0),
      0,
    );
    const totalDownloads = tracks.reduce(
      (sum: number, track: TrackWithStats) => sum + (track.download_count || 0),
      0,
    );

    const topTracks = [...tracks]
      .sort(
        (a: TrackWithStats, b: TrackWithStats) =>
          (b.play_count || 0) - (a.play_count || 0),
      )
      .slice(0, 5)
      .map((track: TrackWithStats) => ({
        id: track.id,
        title: track.title,
        play_count: track.play_count || 0,
        like_count: track.like_count || 0,
      }));

    // Calculate playlist analytics
    const playlistPlays = playlists.reduce(
      (sum: number, playlist: PlaylistWithStats) =>
        sum + (playlist.play_count || 0),
      0,
    );
    const playlistLikes = playlists.reduce(
      (sum: number, playlist: PlaylistWithStats) =>
        sum + (playlist.like_count || 0),
      0,
    );
    const playlistShares = playlists.reduce(
      (sum: number, playlist: PlaylistWithStats) =>
        sum + (playlist.share_count || 0),
      0,
    );

    const topPlaylists = [...playlists]
      .sort(
        (a: PlaylistWithStats, b: PlaylistWithStats) =>
          (b.play_count || 0) - (a.play_count || 0),
      )
      .slice(0, 5)
      .map((playlist: PlaylistWithStats) => ({
        id: playlist.id,
        name: playlist.name,
        play_count: playlist.play_count || 0,
        like_count: playlist.like_count || 0,
      }));

    const endDateObj = endDate ? new Date(endDate) : new Date();
    const startDateObj = startDate
      ? new Date(startDate)
      : (() => {
          const date = new Date();
          date.setDate(date.getDate() - days);
          return date;
        })();

    return {
      tracks: {
        total_tracks: tracks.length,
        total_plays: totalPlays,
        total_likes: totalLikes,
        total_downloads: totalDownloads,
        average_play_count: tracks.length > 0 ? totalPlays / tracks.length : 0,
        top_tracks: topTracks,
      },
      playlists: {
        total_playlists: playlists.length,
        total_plays: playlistPlays,
        total_likes: playlistLikes,
        total_shares: playlistShares,
        average_play_count:
          playlists.length > 0 ? playlistPlays / playlists.length : 0,
        top_playlists: topPlaylists,
      },
      period: {
        start_date: startDateObj.toISOString(),
        end_date: endDateObj.toISOString(),
        days,
      },
    };
  } catch (error) {
    logger.error('Failed to aggregate analytics', {
      error: error instanceof Error ? error.message : String(error),
      stack: error instanceof Error ? error.stack : undefined,
      days,
    });
    return getDefaultAnalytics(days);
  }
}

/**
 * Get default analytics data
 * @param days Number of days
 * @param startDate Optional start date
 * @param endDate Optional end date
 * @returns Default analytics data structure
 */
function getDefaultAnalytics(
  days: number,
  startDate?: string,
  endDate?: string,
): AnalyticsData {
  const endDateObj = endDate ? new Date(endDate) : new Date();
  const startDateObj = startDate
    ? new Date(startDate)
    : (() => {
        const date = new Date();
        date.setDate(date.getDate() - days);
        return date;
      })();

  return {
    tracks: {
      total_tracks: 0,
      total_plays: 0,
      total_likes: 0,
      total_downloads: 0,
      average_play_count: 0,
      top_tracks: [],
    },
    playlists: {
      total_playlists: 0,
      total_plays: 0,
      total_likes: 0,
      total_shares: 0,
      average_play_count: 0,
      top_playlists: [],
    },
    period: {
      start_date: startDateObj.toISOString(),
      end_date: endDateObj.toISOString(),
      days,
    },
  };
}

/**
 * Get track-specific analytics
 * @param trackId Track ID
 * @param days Number of days (default: 30)
 * @returns Track analytics data
 * @throws Error if the request fails
 */
export async function getTrackAnalytics(
  trackId: string,
  days: number = 30,
): Promise<TrackStats> {
  try {
    const response = await apiClient.get<{ dashboard: TrackStats }>(
      `/analytics/tracks/${trackId}`,
      {
        params: { days },
      },
    );
    return response.data.dashboard || response.data;
  } catch (error) {
    logger.error('Failed to fetch track analytics dashboard', {
      error: error instanceof Error ? error.message : String(error),
      trackId,
    });
    if (error instanceof AxiosError) {
      throw new Error(
        error.response?.data?.error ||
          error.message ||
          'Failed to fetch track analytics',
      );
    }
    throw error;
  }
}

/**
 * Get playlist-specific analytics
 * @param playlistId Playlist ID
 * @param days Number of days (default: 30)
 * @returns Playlist analytics data
 * @throws Error if the request fails
 */
export async function getPlaylistAnalytics(
  playlistId: string,
  days: number = 30,
): Promise<PlaylistAnalytics> {
  try {
    const response = await apiClient.get<
      { data: PlaylistAnalytics } | PlaylistAnalytics
    >(`/playlists/${playlistId}/analytics`, {
      params: { days },
    });
    // Handle both wrapped and direct response formats
    if ('data' in response.data && response.data.data) {
      return response.data.data;
    }
    return response.data as PlaylistAnalytics;
  } catch (error) {
    if (error instanceof AxiosError) {
      throw new Error(
        error.response?.data?.error ||
          error.message ||
          'Failed to fetch playlist analytics',
      );
    }
    throw error;
  }
}
