import SwiftUI
import WidgetKit

private struct ActivityEntry: TimelineEntry {
    let date: Date
    let serverName: String
    let rows: [ActivityRow]
    let message: String?
}

private struct PullsEntry: TimelineEntry {
    let date: Date
    let serverName: String
    let rows: [PullRow]
    let message: String?
}

private struct ActivityProvider: AppIntentTimelineProvider {
    func placeholder(in context: Context) -> ActivityEntry {
        ActivityEntry(
            date: .now,
            serverName: "My Server",
            rows: activityPlaceholderRows(for: context.family),
            message: nil
        )
    }

    func snapshot(for configuration: ActivityWidgetIntent, in context: Context) async -> ActivityEntry {
        context.isPreview ? placeholder(in: context) : await load(configuration, family: context.family)
    }

    func timeline(for configuration: ActivityWidgetIntent, in context: Context) async -> Timeline<ActivityEntry> {
        Timeline(
            entries: [await load(configuration, family: context.family)],
            policy: .after(Date.now.addingTimeInterval(15 * 60))
        )
    }

    private func load(_ configuration: ActivityWidgetIntent, family: WidgetFamily) async -> ActivityEntry {
        guard
            let selection = configuration.server,
            let server = WidgetEnvironment.server(for: selection)
        else {
            return ActivityEntry(
                date: .now,
                serverName: "Activity",
                rows: [],
                message: "Add a server in Gotcha, then configure this widget."
            )
        }
        do {
            let limit = activityLimit(for: family)
            let page = try await WidgetEnvironment.core().widgetActivity(serverId: server.id, limit: limit)
            return ActivityEntry(
                date: .now,
                serverName: page.serverName,
                rows: page.rows,
                message: page.rows.isEmpty ? "No recent activity." : nil
            )
        } catch {
            return ActivityEntry(
                date: .now,
                serverName: server.name,
                rows: [],
                message: error.localizedDescription
            )
        }
    }
}

private struct PullsProvider: AppIntentTimelineProvider {
    func placeholder(in context: Context) -> PullsEntry {
        PullsEntry(
            date: .now,
            serverName: "My Server",
            rows: pullPlaceholderRows(for: context.family),
            message: nil
        )
    }

    func snapshot(for configuration: PullsWidgetIntent, in context: Context) async -> PullsEntry {
        context.isPreview ? placeholder(in: context) : await load(configuration, family: context.family)
    }

    func timeline(for configuration: PullsWidgetIntent, in context: Context) async -> Timeline<PullsEntry> {
        Timeline(
            entries: [await load(configuration, family: context.family)],
            policy: .after(Date.now.addingTimeInterval(15 * 60))
        )
    }

    private func load(_ configuration: PullsWidgetIntent, family: WidgetFamily) async -> PullsEntry {
        guard
            let selection = configuration.server,
            let server = WidgetEnvironment.server(for: selection)
        else {
            return PullsEntry(
                date: .now,
                serverName: "Pull Requests",
                rows: [],
                message: "Add a server in Gotcha, then configure this widget."
            )
        }
        do {
            let limit = pullLimit(for: family)
            let page = try await WidgetEnvironment.core().widgetPulls(serverId: server.id, limit: limit)
            return PullsEntry(
                date: .now,
                serverName: page.serverName,
                rows: page.rows,
                message: page.rows.isEmpty ? "No open pull requests." : nil
            )
        } catch {
            return PullsEntry(
                date: .now,
                serverName: server.name,
                rows: [],
                message: error.localizedDescription
            )
        }
    }
}

private func activityLimit(for family: WidgetFamily) -> UInt32 {
    family == .systemMedium ? 3 : 10
}

private func pullLimit(for family: WidgetFamily) -> UInt32 {
    family == .systemMedium ? 2 : 5
}

private func activityPlaceholderRows(for family: WidgetFamily) -> [ActivityRow] {
    let samples: [(ActivityIcon, String, String)] = [
        (.push, "Pushed to main in owner/project", "Just now"),
        (.issue, "Opened an issue in owner/project", "5m ago"),
        (.pullRequest, "Merged a pull request in owner/project", "12m ago"),
        (.branch, "Created branch feature/widget", "25m ago"),
        (.tag, "Published tag v1.0.0", "40m ago"),
        (.release, "Published a release in owner/project", "1h ago"),
        (.repository, "Created repository owner/new-project", "2h ago"),
        (.push, "Pushed to release in owner/project", "3h ago"),
        (.issue, "Closed an issue in owner/project", "4h ago"),
        (.pullRequest, "Opened a pull request in owner/project", "5h ago"),
    ]
    return samples.prefix(Int(activityLimit(for: family))).enumerated().map { index, sample in
        ActivityRow(
            icon: sample.0,
            title: sample.1,
            detail: "",
            meta: sample.2,
            target: .commit,
            owner: "owner",
            repository: "project",
            number: Int64(index),
            sha: ""
        )
    }
}

private func pullPlaceholderRows(for family: WidgetFamily) -> [PullRow] {
    let titles = [
        "Improve the activity screen",
        "Fix repository navigation",
        "Add compact widget layouts",
        "Refresh server configuration",
        "Polish issue filters",
    ]
    return titles.prefix(Int(pullLimit(for: family))).enumerated().map { index, title in
        let number = Int64(42 - index)
        return PullRow(
            number: number,
            owner: "owner",
            repository: "project",
            state: .open,
            title: "project  #\(number)\n\(title)",
            summary: "",
            meta: "Open  ·  developer  ·  \(index * 10)m ago"
        )
    }
}

private struct WidgetHeader: View {
    let title: String
    let symbol: String
    let server: String
    let updated: Date

    var body: some View {
        HStack(alignment: .firstTextBaseline) {
            Label(title, systemImage: symbol)
                .font(.headline)
            Spacer(minLength: 8)
            VStack(alignment: .trailing, spacing: 1) {
                Text(server)
                    .font(.caption.weight(.semibold))
                    .lineLimit(1)
                Text(updated, style: .relative)
                    .font(.caption2)
                    .foregroundStyle(.secondary)
            }
        }
    }
}

private struct ActivityWidgetView: View {
    let entry: ActivityEntry

    var body: some View {
        VStack(alignment: .leading, spacing: 8) {
            WidgetHeader(
                title: "Activity",
                symbol: "waveform.path.ecg",
                server: entry.serverName,
                updated: entry.date
            )
            Divider()
            if let message = entry.message {
                emptyState(message, symbol: "clock.arrow.circlepath")
            } else {
                VStack(alignment: .leading, spacing: 7) {
                    ForEach(Array(entry.rows.enumerated()), id: \.offset) { _, row in
                        HStack(alignment: .firstTextBaseline, spacing: 8) {
                            Image(systemName: symbol(for: row.icon))
                                .foregroundStyle(.tint)
                                .frame(width: 16)
                            Text(row.title)
                                .font(.caption)
                                .lineLimit(1)
                            Spacer(minLength: 4)
                            Text(row.meta)
                                .font(.caption2)
                                .foregroundStyle(.secondary)
                                .lineLimit(1)
                        }
                        .accessibilityElement(children: .combine)
                    }
                }
                .privacySensitive()
            }
        }
        .containerBackground(.background, for: .widget)
        .widgetURL(URL(string: "gotcha://home"))
    }

    private func symbol(for icon: ActivityIcon) -> String {
        switch icon {
        case .repository: "shippingbox"
        case .issue: "exclamationmark.circle"
        case .pullRequest: "arrow.triangle.pull"
        case .branch: "arrow.triangle.branch"
        case .tag: "tag"
        case .push: "arrow.up.circle"
        case .release: "shippingbox.fill"
        }
    }
}

private struct PullsWidgetView: View {
    let entry: PullsEntry

    var body: some View {
        VStack(alignment: .leading, spacing: 8) {
            WidgetHeader(
                title: "Open Pull Requests",
                symbol: "arrow.triangle.pull",
                server: entry.serverName,
                updated: entry.date
            )
            Divider()
            if let message = entry.message {
                emptyState(message, symbol: "arrow.triangle.pull")
            } else {
                VStack(alignment: .leading, spacing: 8) {
                    ForEach(Array(entry.rows.enumerated()), id: \.offset) { _, row in
                        VStack(alignment: .leading, spacing: 2) {
                            Text(row.title)
                                .font(.caption.weight(.semibold))
                                .lineLimit(2)
                            Text(row.meta)
                                .font(.caption2)
                                .foregroundStyle(.secondary)
                                .lineLimit(1)
                        }
                        .accessibilityElement(children: .combine)
                    }
                }
                .privacySensitive()
            }
        }
        .containerBackground(.background, for: .widget)
        .widgetURL(URL(string: "gotcha://pulls"))
    }
}

private func emptyState(_ message: String, symbol: String) -> some View {
    VStack(spacing: 8) {
        Spacer()
        Image(systemName: symbol)
            .font(.title2)
            .foregroundStyle(.secondary)
        Text(message)
            .font(.caption)
            .foregroundStyle(.secondary)
            .multilineTextAlignment(.center)
        Spacer()
    }
    .frame(maxWidth: .infinity)
}

struct ActivityWidget: Widget {
    let kind = "GotchaActivityWidget"

    var body: some WidgetConfiguration {
        AppIntentConfiguration(kind: kind, intent: ActivityWidgetIntent.self, provider: ActivityProvider()) {
            ActivityWidgetView(entry: $0)
        }
        .configurationDisplayName("Recent Activity")
        .description("See the latest activity from a configured Gitea or Forgejo server.")
        .supportedFamilies([.systemMedium, .systemLarge])
    }
}

struct PullsWidget: Widget {
    let kind = "GotchaPullsWidget"

    var body: some WidgetConfiguration {
        AppIntentConfiguration(kind: kind, intent: PullsWidgetIntent.self, provider: PullsProvider()) {
            PullsWidgetView(entry: $0)
        }
        .configurationDisplayName("Open Pull Requests")
        .description("See open pull requests from a configured Gitea or Forgejo server.")
        .supportedFamilies([.systemMedium, .systemLarge])
    }
}

@main
struct GotchaWidgetBundle: WidgetBundle {
    var body: some Widget {
        ActivityWidget()
        PullsWidget()
    }
}
