//
//  SolianPostShuffleWidget.swift
//  SolianWidgetExtension
//
//  Created by LittleSheep on 2026/1/4.
//

import Foundation
import WidgetKit
import SwiftUI

struct SnPostPublisher: Codable {
    let id: String
    let name: String
    let nick: String?
    let description: String?
    let picture: SnCloudFile?
    
    enum CodingKeys: String, CodingKey {
        case id
        case name
        case nick
        case description
        case picture
    }
}

struct SnCloudFile: Codable {
    let id: String
    let url: String?
    let thumbnail: String?
    let mimeType: String?
    
    enum CodingKeys: String, CodingKey {
        case id
        case url
        case thumbnail
        case mimeType = "mimetype"
    }
}

struct SnPost: Codable, Identifiable {
    let id: String
    let title: String?
    let description: String?
    let content: String?
    let publisher: SnPostPublisher
    let tags: [SnPostTag]?
    let createdAt: String?
    let updatedAt: String?
    let attachments: [SnCloudFile]?
    
    var createdDate: Date? {
        guard let createdAt = createdAt else { return nil }

        let formatter = ISO8601DateFormatter()
        formatter.formatOptions = [.withInternetDateTime, .withFractionalSeconds]

        if let date = formatter.date(from: createdAt) {
            return date
        }

        // Fallback for timestamps without fractional seconds
        formatter.formatOptions = [.withInternetDateTime]
        return formatter.date(from: createdAt)
    }
    
    var hasTitle: Bool {
        guard let title = title else { return false }
        return !title.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty
    }
    
    var hasDescription: Bool {
        guard let description = description else { return false }
        return !description.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty
    }
    
    var hasContent: Bool {
        guard let content = content else { return false }
        return !content.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty
    }
    
    var attachmentCount: Int {
        attachments?.count ?? 0
    }
    
    enum CodingKeys: String, CodingKey {
        case id
        case title
        case description
        case content
        case publisher
        case tags
        case createdAt = "created_at"
        case updatedAt = "updated_at"
        case attachments
    }
}

struct SnPostTag: Codable {
    let id: String
    let slug: String
    let name: String?
}

class PostShuffleService {
    private let networkService = WidgetNetworkService()
    
    private lazy var session: URLSession = {
        let configuration = URLSessionConfiguration.ephemeral
        configuration.timeoutIntervalForRequest = 10.0
        configuration.timeoutIntervalForResource = 10.0
        configuration.waitsForConnectivity = false
        return URLSession(configuration: configuration)
    }()
    
    func fetchRandomPost() async throws -> SnPost? {
        guard let token = networkService.token else {
            throw RemoteError.missingCredentials
        }
        
        let baseURL = networkService.baseURL
        guard let url = URL(string: "\(baseURL)/sphere/posts?shuffle=true&take=1") else {
            throw RemoteError.invalidURL
        }
        
        var request = URLRequest(url: url)
        request.httpMethod = "GET"
        request.setValue("AtField \(token)", forHTTPHeaderField: "Authorization")
        request.setValue("application/json", forHTTPHeaderField: "Accept")
        request.timeoutInterval = 10.0
        
        let (data, response) = try await session.data(for: request)
        
        guard let httpResponse = response as? HTTPURLResponse else {
            throw RemoteError.invalidResponse
        }
        
        switch httpResponse.statusCode {
        case 200...299:
            let decoder = JSONDecoder()
            let posts = try decoder.decode([SnPost].self, from: data)
            return posts.first
        case 404:
            return nil
        default:
            throw RemoteError.httpError(httpResponse.statusCode)
        }
    }
}

struct PostShuffleEntry: TimelineEntry {
    let date: Date
    let post: SnPost?
    let error: String?
    let isLoading: Bool
    
    static func placeholder() -> PostShuffleEntry {
        PostShuffleEntry(date: Date(), post: nil, error: nil, isLoading: true)
    }
}

struct PostShuffleProvider: TimelineProvider {
    private let postShuffleService = PostShuffleService()
    
    func placeholder(in context: Context) -> PostShuffleEntry {
        PostShuffleEntry.placeholder()
    }
    
    func getSnapshot(in context: Context, completion: @escaping (PostShuffleEntry) -> ()) {
        Task {
            print("[WidgetKit] [PostShuffleProvider] Getting snapshot...")
            let post = try? await postShuffleService.fetchRandomPost()
            
            print("[WidgetKit] [PostShuffleProvider] Snapshot - Post: \(post != nil ? "Found" : "Not found")")
            
            let entry = PostShuffleEntry(date: Date(), post: post, error: nil, isLoading: false)
            completion(entry)
        }
    }
    
    func getTimeline(in context: Context, completion: @escaping (Timeline<Entry>) -> ()) {
        Task {
            let currentDate = Date()
            print("[WidgetKit] [PostShuffleProvider] Getting timeline at \(currentDate)...")
            
            do {
                let post = try await postShuffleService.fetchRandomPost()
                
                print("[WidgetKit] [PostShuffleProvider] Timeline - Post: \(post != nil ? "Found" : "Not found")")
                
                let entry = PostShuffleEntry(date: currentDate, post: post, error: nil, isLoading: false)
                
                let nextUpdate = Calendar.current.date(byAdding: .minute, value: 10, to: currentDate)!
                print("[WidgetKit] [PostShuffleProvider] Next update at: \(nextUpdate)")
                let timeline = Timeline(entries: [entry], policy: .after(nextUpdate))
                completion(timeline)
            } catch {
                print("[WidgetKit] [PostShuffleProvider] Error in getTimeline: \(error.localizedDescription)")
                let entry = PostShuffleEntry(date: currentDate, post: nil, error: error.localizedDescription, isLoading: false)
                let nextUpdate = Calendar.current.date(byAdding: .minute, value: 5, to: currentDate)!
                let timeline = Timeline(entries: [entry], policy: .after(nextUpdate))
                completion(timeline)
            }
        }
    }
}

struct PostShuffleWidgetEntryView: View {
    var entry: PostShuffleProvider.Entry
    @Environment(\.widgetFamily) var family
    
    var body: some View {
        if let post = entry.post {
            PostContentView(post: post)
        } else if entry.isLoading {
            LoadingView()
        } else if let error = entry.error {
            ErrorView(error: error)
        } else {
            EmptyView()
        }
    }
    
    @ViewBuilder
    private func PostContentView(post: SnPost) -> some View {
        Link(destination: URL(string: "solian://posts/\(post.id)")!) {
            VStack(alignment: .leading, spacing: 8) {
                HStack(alignment: .top, spacing: 8) {
                    if let avatarUrl = post.publisher.picture?.url ?? post.publisher.picture?.thumbnail {
                        AsyncImage(url: URL(string: avatarUrl)) { phase in
                            switch phase {
                            case .success(let image):
                                image
                                    .resizable()
                                    .aspectRatio(contentMode: .fill)
                            case .failure(_):
                                Circle()
                                    .fill(Color.gray.opacity(0.3))
                            default:
                                Circle()
                                    .fill(Color.gray.opacity(0.3))
                                    .overlay(
                                        ProgressView()
                                            .scaleEffect(0.5)
                                    )
                            }
                        }
                        .frame(width: 40, height: 40)
                        .clipShape(Circle())
                    } else {
                        Circle()
                            .fill(Color.gray.opacity(0.3))
                            .frame(width: 40, height: 40)
                    }
                    
                    VStack(alignment: .leading, spacing: 2) {
                        Text(post.publisher.nick ?? post.publisher.name)
                            .font(.headline)
                            .fontWeight(.semibold)
                            .lineLimit(1)
                        
                        if let createdDate = post.createdDate {
                            Text(formatRelativeTime(createdDate))
                                .font(.caption)
                                .foregroundColor(.secondary)
                        }
                    }
                    
                    Spacer()
                }
                
                if post.attachmentCount > 0 {
                    HStack(spacing: 4) {
                        Image(systemName: "paperclip")
                            .font(.caption2)
                            .foregroundColor(.secondary)
                        Text("\(post.attachmentCount)")
                            .font(.caption2)
                            .foregroundColor(.secondary)
                    }
                }
                
                if post.hasTitle {
                    Text(post.title!)
                        .font(family == .systemMedium ? .subheadline : .body)
                        .fontWeight(.semibold)
                        .lineLimit(family == .systemMedium ? 2 : 3)
                }
                
                if post.hasDescription {
                    Text(post.description!)
                        .font(.caption)
                        .foregroundColor(.secondary)
                        .lineLimit(family == .systemMedium ? 2 : 4)
                }
                
                if post.hasContent {
                    Text(post.content!)
                        .font(.caption)
                        .foregroundColor(.primary)
                        .lineLimit(family == .systemMedium ? 3 : 8)
                }
                
                if let tags = post.tags, !tags.isEmpty {
                    let displayTags = Array(tags.prefix(family == .systemMedium ? 2 : 4))
                    ScrollView(.horizontal, showsIndicators: false) {
                        HStack(spacing: 4) {
                            ForEach(displayTags, id: \.id) { tag in
                                Text("#\(tag.name ?? tag.slug)")
                                    .font(.caption2)
                                    .foregroundColor(.blue)
                                    .padding(.horizontal, 6)
                                    .padding(.vertical, 2)
                                    .background(Color.blue.opacity(0.1))
                                    .clipShape(Capsule())
                            }
                        }
                    }
                }
                
                Spacer()
            }
            .padding(12)
        }
    }
    
    @ViewBuilder
    private func EmptyView() -> some View {
        Link(destination: URL(string: "solian://posts/shuffle")!) {
            VStack(alignment: .leading, spacing: 8) {
                HStack(spacing: 6) {
                    Image(systemName: "text.alignleft")
                        .font(.title3)
                        .foregroundColor(.secondary)
                    
                    Spacer()
                }
                
                Text(NSLocalizedString("noPostsAvailable", comment: "No posts available"))
                    .font(.subheadline)
                    .foregroundColor(.secondary)
                
                Spacer()
                
                Text(NSLocalizedString("tapToRefresh", comment: "Tap to refresh"))
                    .font(.caption)
                    .foregroundColor(.secondary)
            }
            .padding(12)
        }
    }
    
    @ViewBuilder
    private func LoadingView() -> some View {
        VStack(alignment: .leading, spacing: 8) {
            HStack(spacing: 6) {
                ProgressView()
                    .scaleEffect(0.8)
                Text(NSLocalizedString("loadingPost", comment: "Loading post..."))
                    .font(.subheadline)
                    .foregroundColor(.secondary)
                Spacer()
            }
            
            Spacer()
        }
        .padding(12)
    }
    
    @ViewBuilder
    private func ErrorView(error: String) -> some View {
        Link(destination: URL(string: "solian://posts/shuffle")!) {
            VStack(alignment: .leading, spacing: 8) {
                HStack(spacing: 6) {
                    Image(systemName: "exclamationmark.triangle")
                        .font(.title3)
                        .foregroundColor(.secondary)
                    
                    Text(NSLocalizedString("error", comment: "Error"))
                        .font(.headline)
                    
                    Spacer()
                }
                
                Text(NSLocalizedString("openAppToRefresh", comment: "Open app to refresh"))
                    .font(.caption)
                    .foregroundColor(.secondary)
                
                Text(error)
                    .font(.footnote)
                    .foregroundStyle(.secondary)
                    .lineLimit(5)
                    .multilineTextAlignment(.leading)
                
                Spacer()
            }
            .padding(12)
        }
    }
    
    private func formatRelativeTime(_ date: Date) -> String {
        let now = Date()
        let interval = now.timeIntervalSince(date)
        
        if interval < 60 {
            return NSLocalizedString("justNow", comment: "Just now")
        } else if interval < 3600 {
            let minutes = Int(interval / 60)
            return String(format: NSLocalizedString("minutesAgo", comment: "%d min ago"), minutes)
        } else if interval < 86400 {
            let hours = Int(interval / 3600)
            return String(format: NSLocalizedString("hoursAgo", comment: "%d hr ago"), hours)
        } else {
            let days = Int(interval / 86400)
            return String(format: NSLocalizedString("daysAgo", comment: "%d d ago"), days)
        }
    }
}

struct PostShuffleWidgetRootView: View {
    var entry: PostShuffleProvider.Entry
    @Environment(\.colorScheme) var colorScheme
    
    var body: some View {
        if #available(iOS 17.0, *) {
            ZStack {
                PostShuffleWidgetEntryView(entry: entry)
                
                if entry.post != nil {
                    GeometryReader { geometry in
                        Image(colorScheme == .dark ? "CloudyLambDark" : "CloudyLamb")
                            .resizable()
                            .aspectRatio(contentMode: .fit)
                            .frame(
                                width: geometry.size.width * 0.9,
                                height: geometry.size.width * 0.9
                            )
                            .opacity(0.12)
                            .mask(
                                LinearGradient(
                                    gradient: Gradient(colors: [
                                        Color.white,
                                        Color.white,
                                        Color.clear
                                    ]),
                                    startPoint: .topLeading,
                                    endPoint: .bottomTrailing
                                )
                            )
                            .position(
                                x: geometry.size.width * 0.9,
                                y: 20
                            )
                    }
                    .allowsHitTesting(false)
                }
            }
            .containerBackground(.fill.tertiary, for: .widget)
            .padding(.vertical, 8)
        } else {
            PostShuffleWidgetEntryView(entry: entry)
                .padding()
                .background()
        }
    }
}

struct SolianPostShuffleWidget: Widget {
    let kind: String = "SolianPostShuffleWidget"
    
    var body: some WidgetConfiguration {
        StaticConfiguration(kind: kind, provider: PostShuffleProvider()) { entry in
            PostShuffleWidgetRootView(entry: entry)
        }
        .configurationDisplayName("Random Post")
        .description("Discover a random post from the network")
        .supportedFamilies(supportedFamilies)
    }
    
    private var supportedFamilies: [WidgetFamily] {
        return [.systemMedium, .systemLarge]
    }
}

#Preview(as: .systemMedium) {
    SolianPostShuffleWidget()
} timeline: {
    PostShuffleEntry(
        date: .now,
        post: SnPost(
            id: "test-post-id",
            title: "Hello World!",
            description: "This is a test post description",
            content: "This is a content of a test post. It can be longer and show more text in the widget.",
            publisher: SnPostPublisher(
                id: "publisher-1",
                name: "Test Publisher",
                nick: "Testy",
                description: "A test publisher",
                picture: nil
            ),
            tags: [
                SnPostTag(id: "tag-1", slug: "test", name: "Test"),
                SnPostTag(id: "tag-2", slug: "example", name: "Example")
            ],
            createdAt: ISO8601DateFormatter().string(from: Date()),
            updatedAt: ISO8601DateFormatter().string(from: Date()),
            attachments: []
        ),
        error: nil,
        isLoading: false
    )
}

#Preview(as: .systemLarge) {
    SolianPostShuffleWidget()
} timeline: {
    PostShuffleEntry(
        date: .now,
        post: SnPost(
            id: "test-post-id",
            title: "Welcome to the Solar Network!",
            description: "This is a test post description that is a bit longer to demonstrate the widget layout with various content types",
            content: "This is content of a test post. It can be longer and show more text in the widget. The large widget should display more content and allow for better reading experience. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.",
            publisher: SnPostPublisher(
                id: "publisher-1",
                name: "Test Publisher",
                nick: "Testy McTestface",
                description: "A test publisher",
                picture: nil
            ),
            tags: [
                SnPostTag(id: "tag-1", slug: "test", name: "Test"),
                SnPostTag(id: "tag-2", slug: "example", name: "Example"),
                SnPostTag(id: "tag-3", slug: "widget", name: "Widget"),
                SnPostTag(id: "tag-4", slug: "ios", name: "iOS")
            ],
            createdAt: ISO8601DateFormatter().string(from: Date()),
            updatedAt: ISO8601DateFormatter().string(from: Date()),
            attachments: [
                SnCloudFile(id: "file-1", url: nil, thumbnail: nil, mimeType: "image/jpeg"),
                SnCloudFile(id: "file-2", url: nil, thumbnail: nil, mimeType: "image/png")
            ]
        ),
        error: nil,
        isLoading: false
    )
}
