//
//  AdwaitaAboutDialogConfig.swift
//  Adwaita
//
//  Created by lambdaclan on 09.01.2026.
//

import CAdw
import Foundation

/// A link shown in the About dialog.
public struct AboutLink {

    /// The link title.
    public var title: String

    /// The destination URL.
    public var url: URL?

    /// Create a new link.
    /// - Parameters:
    ///   - title: The link title.
    ///   - url: The destination URL.
    public init(title: String, url: URL?) {
        self.title = title
        self.url = url
    }

}

/// The type of contribution made by a credited person.
public enum ContributionRole {

    /// A person who contributed to development.
    case developer
    /// A person who contributed to design.
    case designer
    /// A person who contributed artwork or visuals.
    case artist
    /// A person who contributed translations.
    case translator

}

/// A credited person and their contribution role.
public struct CreditEntry {

    /// The contributor's role.
    public var role: ContributionRole

    /// The contributor's name.
    public var name: String

    /// Create a new credit entry.
    /// - Parameters:
    ///   - role: The contributor's role.
    ///   - name: The contributor's name.
    public init(role: ContributionRole, name: String) {
        self.role = role
        self.name = name
    }

}

/// An acknowledgement entry.
public struct AcknowledgementEntry {

    /// The acknowledgement section title.
    public var title: String

    /// Acknowledged person/organization name.
    public var name: String

    /// Create a new acknowledgement entry.
    /// - Parameters:
    ///   - title: The acknowledgement section title.
    ///   - name: The acknowledged person or organization.
    public init(title: String, name: String) {
        self.title = title
        self.name = name
    }

}

/// Other (additional) application entry.
public struct OtherAppEntry {

    /// The application identifier of the referenced app.
    public var appID: String

    /// The display name of the referenced app.
    public var name: String

    /// A short descriptive summary of the referenced app.
    public var summary: String

    /// Creates a new other application entry.
    /// - Parameters:
    ///   - appID: The application identifier of the referenced app.
    ///   - name: The display name of the referenced app.
    ///   - summary: A short descriptive summary of the referenced app.
    public init(appID: String, name: String, summary: String) {
        self.appID = appID
        self.name = name
        self.summary = summary
    }

}

/// Initialization options for the about dialog wrapper.
public struct AdwaitaAboutDialogConfig {

    /// The app's name.
    public var appName: String?
    /// The developer's name.
    public var developer: String?
    /// The app version.
    public var version: String?
    /// The app icon.
    public var icon: Icon?
    /// The app's website.
    public var website: URL?
    /// The link for opening issues.
    public var issues: URL?
    /// The link for getting support.
    public var support: URL?
    /// Additional links related to the app.
    public var links: [AboutLink]?
    /// The app's copyright information.
    public var copyright: String?
    /// The app's license.
    public var license: String?
    /// The app's release notes.
    public var releaseNotes: String?
    /// The comments about the application.
    public var comments: String?
    /// Recognition by name and role of contributors.
    public var credits: [CreditEntry]?
    /// Acknowledgements to display in the dialog.
    public var acknowledgements: [AcknowledgementEntry]?
    /// Additional applications.
    public var otherApps: [OtherAppEntry]?

    /// Initialize the about dialog wrapper.
    /// - Parameters:
    ///     - appName: The app's name.
    ///     - developer: The developer's name.
    ///     - version: The version string.
    ///     - icon: The app icon.
    ///     - website: The app's website.
    ///     - issues: Website for reporting issues.
    ///     - support: Website for getting support.
    ///     - links: Additional links related to the app.
    ///     - copyright: The app's copyright information.
    ///     - license: The app's license.
    ///     - releaseNotes: The app's release notes.
    ///     - comments: The comments about the application.
    ///     - credits: Recognition by name and role of contributors.
    ///     - acknowledgements: List of acknowledgements.
    ///     - otherApps: List of other applications.
    public init(
        appName: String? = nil,
        developer: String? = nil,
        version: String? = nil,
        icon: Icon? = nil,
        website: URL? = nil,
        issues: URL? = nil,
        support: URL? = nil,
        links: [AboutLink]? = nil,
        copyright: String? = nil,
        license: String? = nil,
        releaseNotes: String? = nil,
        comments: String? = nil,
        credits: [CreditEntry]? = nil,
        acknowledgements: [AcknowledgementEntry]? = nil,
        otherApps: [OtherAppEntry]? = nil
    ) {
        self.appName = appName
        self.developer = developer
        self.version = version
        self.icon = icon
        self.website = website
        self.issues = issues
        self.support = support
        self.links = links
        self.copyright = copyright
        self.license = license
        self.releaseNotes = releaseNotes
        self.comments = comments
        self.credits = credits
        self.acknowledgements = acknowledgements
        self.otherApps = otherApps
    }

    /// Applies a string value to the dialog using the given setter.
    /// - Parameters:
    ///   - value: The optional string to apply.
    ///   - setter: The C function that sets the value on the dialog.
    ///   - dialog: The dialog instance.
    @inline(__always)
    private func set(
        _ value: String?,
        using setter: (OpaquePointer, UnsafePointer<CChar>?) -> Void,
        on dialog: OpaquePointer
    ) {
        guard let value else {
            return
        }

        setter(dialog, value)
    }

    /// Applies a list of links to the dialog.
    /// - Parameters:
    ///   - links: The optional list of links.
    ///   - dialog: The dialog instance.
    @inline(__always)
    private func set(
        _ links: [AboutLink]?,
        on dialog: OpaquePointer
    ) {
        links?.forEach { link in
            adw_about_dialog_add_link(dialog, link.title, link.url?.absoluteString)
        }
    }

    /// Applies credit entries to the dialog.
    /// - Parameters:
    ///   - credits: The optional list of credit entries.
    ///   - dialog: The dialog instance.
    @inline(__always)
    private func set(_ credits: [CreditEntry]?, on dialog: OpaquePointer) {
        guard let credits else {
             return
        }

        let grouped = Dictionary(grouping: credits, by: \.role)

        if let devs = grouped[.developer]?.map(\.name),
        let ptr = devs.cMutableArray {
            adw_about_dialog_set_developers(dialog, ptr)
        }

        if let designers = grouped[.designer]?.map(\.name),
        let ptr = designers.cMutableArray {
            adw_about_dialog_set_designers(dialog, ptr)
        }

        if let artists = grouped[.artist]?.map(\.name),
        let ptr = artists.cMutableArray {
            adw_about_dialog_set_artists(dialog, ptr)
        }

        if let translators = grouped[.translator]?.map(\.name),
        !translators.isEmpty {
            let joined = translators.joined(separator: "\n")
            adw_about_dialog_set_translator_credits(dialog, joined)
        }
    }

    /// Applies acknowledgement entries to the dialog.
    /// - Parameters:
    ///   - acknowledgements: The optional list of acknowledgement entries.
    ///   - dialog: The dialog instance.
    @inline(__always)
    private func set(
        _ acknowledgements: [AcknowledgementEntry]?,
        on dialog: OpaquePointer
    ) {
        guard let acknowledgements else {
            return
        }

        let grouped = Dictionary(grouping: acknowledgements) { $0.title }

        for (title, entries) in grouped {
            let names = entries.map { $0.name }
            guard let people = names.cMutableArray else { continue }

            adw_about_dialog_add_acknowledgement_section(dialog, title, people)
        }
    }

    /// Applies other apps entries to the dialog.
    /// - Parameters:
    ///   - otherApps: The optional list of other app entries.
    ///   - dialog: The dialog instance.
    @inline(__always)
    private func set(
        _ otherApps: [OtherAppEntry]?,
        on dialog: OpaquePointer
    ) {
        guard let otherApps else {
            return
        }

        for entry in otherApps {
            adw_about_dialog_add_other_app(
                dialog,
                entry.appID,
                entry.name,
                entry.summary
            )
        }
      }

    /// Apply the configuration values to the given dialog.
    /// - Parameters:
    ///     - dialog: The underlying Adwaita dialog instance to update with the configuration.
    func apply(to dialog: OpaquePointer) {
        set(appName, using: adw_about_dialog_set_application_name, on: dialog)
        set(developer, using: adw_about_dialog_set_developer_name, on: dialog)
        set(version, using: adw_about_dialog_set_version, on: dialog)
        set(icon?.string, using: adw_about_dialog_set_application_icon, on: dialog)
        set(website?.absoluteString, using: adw_about_dialog_set_website, on: dialog)
        set(issues?.absoluteString, using: adw_about_dialog_set_issue_url, on: dialog)
        set(support?.absoluteString, using: adw_about_dialog_set_support_url, on: dialog)
        set(copyright, using: adw_about_dialog_set_copyright, on: dialog)
        set(license, using: adw_about_dialog_set_license, on: dialog)
        set(releaseNotes, using: adw_about_dialog_set_release_notes, on: dialog)
        set(comments, using: adw_about_dialog_set_comments, on: dialog)
        set(links, on: dialog)
        set(credits, on: dialog)
        set(acknowledgements, on: dialog)
        set(otherApps, on: dialog)
    }

}
