import Foundation

struct AnalyticsCalculator {
    static func calculateAnalytics(for links: [Link], timeRange: AnalyticsView.TimeRange, metric: AnalyticsView.Metric) -> AnalyticsData {
        let filteredData = filterDataByTimeRange(links: links, timeRange: timeRange)

        return AnalyticsData(
            totalLinks: filteredData.totalLinks,
            totalClicks: filteredData.totalClicks,
            activeLinks: filteredData.activeLinks,
            averageClicksPerLink: filteredData.averageClicksPerLink,
            linksChange: calculatePercentageChange(current: Double(filteredData.totalLinks), previous: Double(filteredData.previousTotalLinks)),
            clicksChange: calculatePercentageChange(current: Double(filteredData.totalClicks), previous: Double(filteredData.previousTotalClicks)),
            avgClicksChange: calculatePercentageChange(current: filteredData.averageClicksPerLink, previous: filteredData.previousAverageClicksPerLink),
            activeLinksChange: calculatePercentageChange(current: Double(filteredData.activeLinks), previous: Double(filteredData.previousActiveLinks)),
            chartData: generateChartData(for: links, timeRange: timeRange, metric: metric),
            topLinks: getTopLinks(from: links, timeRange: timeRange),
            linkTypeDistribution: getLinkTypeDistribution(from: links, timeRange: timeRange),
            recentActivity: getRecentActivity(from: links, timeRange: timeRange)
        )
    }

    private static func filterDataByTimeRange(links: [Link], timeRange: AnalyticsView.TimeRange) -> FilteredAnalyticsData {
        let calendar = Calendar.current

        // Current period
        let currentPeriodLinks: [Link]
        let currentClickLogs: [ClickLog]

        if let dateRange = timeRange.dateRange {
            currentPeriodLinks = links.filter { link in
                dateRange.contains(link.createdAt)
            }

            currentClickLogs = links.flatMap { link in
                link.sortedClickLogs.filter { clickLog in
                    dateRange.contains(clickLog.clickedAt)
                }
            }
        } else {
            // All time
            currentPeriodLinks = links
            currentClickLogs = links.flatMap { $0.sortedClickLogs }
        }

        // Previous period (for comparison)
        let previousPeriodLinks: [Link]
        let previousClickLogs: [ClickLog]

        if let dateRange = timeRange.dateRange {
            let duration = dateRange.duration
            let previousStartDate = calendar.date(byAdding: .second, value: -Int(duration), to: dateRange.start)!
            let previousEndDate = dateRange.start
            let previousDateRange = DateInterval(start: previousStartDate, end: previousEndDate)

            previousPeriodLinks = links.filter { link in
                previousDateRange.contains(link.createdAt)
            }

            previousClickLogs = links.flatMap { link in
                link.sortedClickLogs.filter { clickLog in
                    previousDateRange.contains(clickLog.clickedAt)
                }
            }
        } else {
            // For all time, compare with half the data as "previous"
            let midpoint = links.count / 2
            previousPeriodLinks = Array(links.prefix(midpoint))
            previousClickLogs = previousPeriodLinks.flatMap { $0.sortedClickLogs }
        }

        // Calculate metrics
        let currentTotalLinks = currentPeriodLinks.count
        let currentTotalClicks = currentClickLogs.count
        let currentActiveLinks = currentPeriodLinks.filter { $0.clickCount > 0 }.count
        let currentAverageClicksPerLink = currentTotalLinks > 0 ? Double(currentTotalClicks) / Double(currentTotalLinks) : 0.0

        let previousTotalLinks = previousPeriodLinks.count
        let previousTotalClicks = previousClickLogs.count
        let previousActiveLinks = previousPeriodLinks.filter { $0.clickCount > 0 }.count
        let previousAverageClicksPerLink = previousTotalLinks > 0 ? Double(previousTotalClicks) / Double(previousTotalLinks) : 0.0

        return FilteredAnalyticsData(
            totalLinks: currentTotalLinks,
            totalClicks: currentTotalClicks,
            activeLinks: currentActiveLinks,
            averageClicksPerLink: currentAverageClicksPerLink,
            previousTotalLinks: previousTotalLinks,
            previousTotalClicks: previousTotalClicks,
            previousActiveLinks: previousActiveLinks,
            previousAverageClicksPerLink: previousAverageClicksPerLink
        )
    }

    private static func calculatePercentageChange(current: Double, previous: Double) -> Double? {
        guard previous > 0 else { return nil }
        return ((current - previous) / previous) * 100
    }

    private static func generateChartData(for links: [Link], timeRange: AnalyticsView.TimeRange, metric: AnalyticsView.Metric) -> [ChartDataPoint] {
        switch metric {
        case .clicks:
            return generateClicksChartData(for: links, timeRange: timeRange)
        case .links:
            return generateLinksChartData(for: links, timeRange: timeRange)
        case .topLinks:
            return generateTopLinksChartData(for: links, timeRange: timeRange)
        }
    }

    private static func generateClicksChartData(for links: [Link], timeRange: AnalyticsView.TimeRange) -> [ChartDataPoint] {
        let calendar = Calendar.current
        let allClickLogs: [ClickLog]
        let dateRange: DateInterval

        if let range = timeRange.dateRange {
            dateRange = range
            allClickLogs = links.flatMap { link in
                link.sortedClickLogs.filter { clickLog in
                    range.contains(clickLog.clickedAt)
                }
            }
        } else {
            // All time - create a range from first click to now
            let allClicks = links.flatMap { $0.sortedClickLogs }
            guard let firstClick = allClicks.min(by: { $0.clickedAt < $1.clickedAt }) else {
                return []
            }
            dateRange = DateInterval(start: firstClick.clickedAt, end: Date())
            allClickLogs = allClicks
        }

        // Determine the grouping interval
        let component: Calendar.Component
        let dateFormat: String

        let duration = dateRange.duration
        if duration <= 7 * 24 * 3600 { // 1 week
            component = .day
            dateFormat = "MMM d"
        } else if duration <= 90 * 24 * 3600 { // 3 months
            component = .day
            dateFormat = "MMM d"
        } else if duration <= 365 * 24 * 3600 { // 1 year
            component = .weekOfYear
            dateFormat = "MMM d"
        } else {
            component = .month
            dateFormat = "MMM yyyy"
        }

        // Group clicks by time interval
        let groupedClicks = Dictionary(grouping: allClickLogs) { clickLog in
            calendar.dateInterval(of: component, for: clickLog.clickedAt)?.start ?? clickLog.clickedAt
        }

        // Create chart data points
        let formatter = DateFormatter()
        formatter.dateFormat = dateFormat

        return groupedClicks.map { date, clicks in
            ChartDataPoint(
                date: date,
                count: clicks.count,
                label: formatter.string(from: date)
            )
        }.sorted { $0.date < $1.date }
    }

    private static func generateLinksChartData(for links: [Link], timeRange: AnalyticsView.TimeRange) -> [ChartDataPoint] {
        let calendar = Calendar.current
        let filteredLinks: [Link]
        let dateRange: DateInterval

        if let range = timeRange.dateRange {
            dateRange = range
            filteredLinks = links.filter { link in
                range.contains(link.createdAt)
            }
        } else {
            // All time
            guard let firstLink = links.min(by: { $0.createdAt < $1.createdAt }) else {
                return []
            }
            dateRange = DateInterval(start: firstLink.createdAt, end: Date())
            filteredLinks = links
        }

        // Determine the grouping interval
        let component: Calendar.Component
        let dateFormat: String

        let duration = dateRange.duration
        if duration <= 30 * 24 * 3600 { // 1 month
            component = .day
            dateFormat = "MMM d"
        } else if duration <= 365 * 24 * 3600 { // 1 year
            component = .weekOfYear
            dateFormat = "MMM d"
        } else {
            component = .month
            dateFormat = "MMM yyyy"
        }

        // Group links by creation date
        let groupedLinks = Dictionary(grouping: filteredLinks) { link in
            calendar.dateInterval(of: component, for: link.createdAt)?.start ?? link.createdAt
        }

        // Create chart data points
        let formatter = DateFormatter()
        formatter.dateFormat = dateFormat

        return groupedLinks.map { date, links in
            ChartDataPoint(
                date: date,
                count: links.count,
                label: formatter.string(from: date)
            )
        }.sorted { $0.date < $1.date }
    }

    private static func generateTopLinksChartData(for links: [Link], timeRange: AnalyticsView.TimeRange) -> [ChartDataPoint] {
        let topLinks = getTopLinks(from: links, timeRange: timeRange)

        return topLinks.enumerated().map { index, linkData in
            ChartDataPoint(
                date: Date(), // Not used for top links chart
                count: linkData.clicks,
                label: linkData.link.alias
            )
        }
    }

    private static func getTopLinks(from links: [Link], timeRange: AnalyticsView.TimeRange, limit: Int = 10) -> [TopLinkData] {
        let filteredLinks: [Link]

        if let dateRange = timeRange.dateRange {
            // Filter clicks within the time range
            filteredLinks = links.map { link in
                let filteredClickLogs = link.sortedClickLogs.filter { clickLog in
                    dateRange.contains(clickLog.clickedAt)
                }

                // Create a temporary link-like structure with filtered click count
                return (link, filteredClickLogs.count)
            }
            .filter { $0.1 > 0 } // Only include links with clicks in the period
            .sorted { $0.1 > $1.1 } // Sort by click count
            .prefix(limit)
            .map { $0.0 } // Extract the link
        } else {
            // All time
            filteredLinks = Array(links.sorted { $0.clickCount > $1.clickCount }.prefix(limit))
        }

        return filteredLinks.map { link in
            let clickCount: Int
            if let dateRange = timeRange.dateRange {
                clickCount = link.sortedClickLogs.filter { dateRange.contains($0.clickedAt) }.count
            } else {
                clickCount = Int(link.clickCount)
            }

            return TopLinkData(link: link, clicks: clickCount)
        }
    }

    private static func getLinkTypeDistribution(from links: [Link], timeRange: AnalyticsView.TimeRange) -> [LinkTypeData] {
        let filteredLinks: [Link]

        if let dateRange = timeRange.dateRange {
            filteredLinks = links.filter { link in
                dateRange.contains(link.createdAt)
            }
        } else {
            filteredLinks = links
        }

        let typeGroups = Dictionary(grouping: filteredLinks) { $0.linkTypeEnum }

        return typeGroups.map { type, links in
            LinkTypeData(type: type, count: links.count)
        }.sorted { $0.count > $1.count }
    }

    private static func getRecentActivity(from links: [Link], timeRange: AnalyticsView.TimeRange, limit: Int = 20) -> [RecentActivity] {
        var activities: [RecentActivity] = []

        // Add link creation activities
        let filteredLinks: [Link]
        if let dateRange = timeRange.dateRange {
            filteredLinks = links.filter { dateRange.contains($0.createdAt) }
        } else {
            filteredLinks = Array(links.sorted { $0.createdAt > $1.createdAt }.prefix(limit / 2))
        }

        for link in filteredLinks {
            activities.append(RecentActivity(
                id: UUID(),
                type: .linkCreated,
                description: "Created link '\(link.alias)'",
                timestamp: link.createdAt,
                link: link
            ))
        }

        // Add click activities
        let allClickLogs: [ClickLog]
        if let dateRange = timeRange.dateRange {
            allClickLogs = links.flatMap { link in
                link.sortedClickLogs.filter { dateRange.contains($0.clickedAt) }
            }
        } else {
            allClickLogs = links.flatMap { $0.sortedClickLogs }.sorted { $0.clickedAt > $1.clickedAt }
        }

        for clickLog in Array(allClickLogs.prefix(limit / 2)) {
            activities.append(RecentActivity(
                id: UUID(),
                type: .linkClicked,
                description: "Clicked link '\(clickLog.link.alias)'",
                timestamp: clickLog.clickedAt,
                link: clickLog.link
            ))
        }

        return Array(activities.sorted { $0.timestamp > $1.timestamp }.prefix(limit))
    }
}

// MARK: - Supporting Data Structures

struct AnalyticsData {
    let totalLinks: Int
    let totalClicks: Int
    let activeLinks: Int
    let averageClicksPerLink: Double
    let linksChange: Double?
    let clicksChange: Double?
    let avgClicksChange: Double?
    let activeLinksChange: Double?
    let chartData: [ChartDataPoint]
    let topLinks: [TopLinkData]
    let linkTypeDistribution: [LinkTypeData]
    let recentActivity: [RecentActivity]
}

private struct FilteredAnalyticsData {
    let totalLinks: Int
    let totalClicks: Int
    let activeLinks: Int
    let averageClicksPerLink: Double
    let previousTotalLinks: Int
    let previousTotalClicks: Int
    let previousActiveLinks: Int
    let previousAverageClicksPerLink: Double
}

struct TopLinkData {
    let link: Link
    let clicks: Int
}

struct LinkTypeData {
    let type: LinkType
    let count: Int
}

struct RecentActivity: Identifiable {
    let id: UUID
    let type: ActivityType
    let description: String
    let timestamp: Date
    let link: Link?

    enum ActivityType {
        case linkCreated
        case linkUpdated
        case linkClicked
        case linkDeleted

        var icon: String {
            switch self {
            case .linkCreated:
                return "plus.circle"
            case .linkUpdated:
                return "pencil.circle"
            case .linkClicked:
                return "hand.tap"
            case .linkDeleted:
                return "trash.circle"
            }
        }

        var color: Color {
            switch self {
            case .linkCreated:
                return .green
            case .linkUpdated:
                return .blue
            case .linkClicked:
                return .orange
            case .linkDeleted:
                return .red
            }
        }
    }
}

import SwiftUI
