//
//  ShareableContentTests.swift
//  SportsTimeTests
//
//  TDD specification tests for ShareableContent types.
//

import Testing
import Foundation
import SwiftUI
@testable import SportsTime

// MARK: - ShareCardType Tests

@Suite("ShareCardType")
struct ShareCardTypeTests {

    // MARK: - Specification Tests: CaseIterable

    /// - Expected Behavior: Includes all expected card types
    @Test("allCases: includes all card types")
    func allCases_includesAll() {
        let allTypes = ShareCardType.allCases

        #expect(allTypes.contains(.tripSummary))
        #expect(allTypes.contains(.achievementSpotlight))
        #expect(allTypes.contains(.achievementCollection))
        #expect(allTypes.contains(.stadiumProgress))
    }

    // MARK: - Invariant Tests

    /// - Invariant: Each type has a unique rawValue
    @Test("Invariant: unique rawValues")
    func invariant_uniqueRawValues() {
        let allTypes = ShareCardType.allCases
        let rawValues = allTypes.map { $0.rawValue }
        let uniqueRawValues = Set(rawValues)

        #expect(rawValues.count == uniqueRawValues.count)
    }

    /// - Invariant: Count matches expected number
    @Test("Invariant: correct count")
    func invariant_correctCount() {
        #expect(ShareCardType.allCases.count == 4)
    }
}

// MARK: - ShareTheme Tests

@Suite("ShareTheme")
struct ShareThemeTests {

    // MARK: - Specification Tests: Static Themes

    /// - Expected Behavior: All preset themes are accessible
    @Test("Static themes: all presets exist")
    func staticThemes_allExist() {
        // Access each theme to ensure they exist
        let _ = ShareTheme.dark
        let _ = ShareTheme.light
        let _ = ShareTheme.midnight
        let _ = ShareTheme.forest
        let _ = ShareTheme.sunset
        let _ = ShareTheme.berry
        let _ = ShareTheme.ocean
        let _ = ShareTheme.slate

        #expect(true) // If we got here, all themes exist
    }

    /// - Expected Behavior: all array contains all themes
    @Test("all: contains all preset themes")
    func all_containsAllThemes() {
        let all = ShareTheme.all

        #expect(all.count == 8)
        #expect(all.contains(where: { $0.id == "dark" }))
        #expect(all.contains(where: { $0.id == "light" }))
        #expect(all.contains(where: { $0.id == "midnight" }))
        #expect(all.contains(where: { $0.id == "forest" }))
        #expect(all.contains(where: { $0.id == "sunset" }))
        #expect(all.contains(where: { $0.id == "berry" }))
        #expect(all.contains(where: { $0.id == "ocean" }))
        #expect(all.contains(where: { $0.id == "slate" }))
    }

    // MARK: - Specification Tests: theme(byId:)

    /// - Expected Behavior: Returns matching theme by id
    @Test("theme(byId:): returns matching theme")
    func themeById_returnsMatching() {
        let dark = ShareTheme.theme(byId: "dark")
        let light = ShareTheme.theme(byId: "light")

        #expect(dark.id == "dark")
        #expect(light.id == "light")
    }

    /// - Expected Behavior: Returns dark theme for unknown id
    @Test("theme(byId:): returns dark for unknown id")
    func themeById_unknownReturnsDark() {
        let unknown = ShareTheme.theme(byId: "nonexistent")
        #expect(unknown.id == "dark")
    }

    /// - Expected Behavior: Finds all valid themes by id
    @Test("theme(byId:): finds all valid themes")
    func themeById_findsAllValid() {
        let ids = ["dark", "light", "midnight", "forest", "sunset", "berry", "ocean", "slate"]

        for id in ids {
            let theme = ShareTheme.theme(byId: id)
            #expect(theme.id == id)
        }
    }

    // MARK: - Specification Tests: Properties

    /// - Expected Behavior: Each theme has required properties
    @Test("Properties: themes have all required fields")
    func properties_allRequired() {
        for theme in ShareTheme.all {
            #expect(!theme.id.isEmpty)
            #expect(!theme.name.isEmpty)
            #expect(theme.gradientColors.count >= 2)
            // Colors exist (can't easily test Color values)
        }
    }

    // MARK: - Invariant Tests

    /// - Invariant: All themes have unique ids
    @Test("Invariant: unique theme ids")
    func invariant_uniqueIds() {
        let ids = ShareTheme.all.map { $0.id }
        let uniqueIds = Set(ids)

        #expect(ids.count == uniqueIds.count)
    }

    /// - Invariant: All themes are Hashable and Identifiable
    @Test("Invariant: themes are Hashable")
    func invariant_hashable() {
        var themeSet: Set<ShareTheme> = []

        for theme in ShareTheme.all {
            themeSet.insert(theme)
        }

        #expect(themeSet.count == ShareTheme.all.count)
    }
}

// MARK: - ShareError Tests

@Suite("ShareError")
struct ShareErrorTests {

    // MARK: - Specification Tests: errorDescription

    /// - Expected Behavior: renderingFailed explains render failure
    @Test("errorDescription: renderingFailed mentions render")
    func errorDescription_renderingFailed() {
        let error = ShareError.renderingFailed
        #expect(error.errorDescription != nil)
        #expect(error.errorDescription!.lowercased().contains("render") || error.errorDescription!.lowercased().contains("failed"))
    }

    /// - Expected Behavior: mapSnapshotFailed explains snapshot failure
    @Test("errorDescription: mapSnapshotFailed mentions map")
    func errorDescription_mapSnapshotFailed() {
        let error = ShareError.mapSnapshotFailed
        #expect(error.errorDescription != nil)
        #expect(error.errorDescription!.lowercased().contains("map") || error.errorDescription!.lowercased().contains("snapshot"))
    }

    /// - Expected Behavior: instagramNotInstalled explains Instagram requirement
    @Test("errorDescription: instagramNotInstalled mentions Instagram")
    func errorDescription_instagramNotInstalled() {
        let error = ShareError.instagramNotInstalled
        #expect(error.errorDescription != nil)
        #expect(error.errorDescription!.lowercased().contains("instagram"))
    }

    // MARK: - Invariant Tests

    /// - Invariant: All errors have non-empty descriptions
    @Test("Invariant: all errors have descriptions")
    func invariant_allHaveDescriptions() {
        let errors: [ShareError] = [
            .renderingFailed,
            .mapSnapshotFailed,
            .instagramNotInstalled
        ]

        for error in errors {
            #expect(error.errorDescription != nil)
            #expect(!error.errorDescription!.isEmpty)
        }
    }
}

// MARK: - ShareCardDimensions Tests

@Suite("ShareCardDimensions")
struct ShareCardDimensionsTests {

    // MARK: - Specification Tests: Static Constants

    /// - Expected Behavior: Card size is standard Instagram story size
    @Test("cardSize: is 1080x1920")
    func cardSize_instagramStory() {
        #expect(ShareCardDimensions.cardSize.width == 1080)
        #expect(ShareCardDimensions.cardSize.height == 1920)
    }

    /// - Expected Behavior: Map snapshot size fits within card with padding
    @Test("mapSnapshotSize: has reasonable dimensions")
    func mapSnapshotSize_reasonable() {
        #expect(ShareCardDimensions.mapSnapshotSize.width == 960)
        #expect(ShareCardDimensions.mapSnapshotSize.height == 480)
    }

    /// - Expected Behavior: Route map size fits within card with padding
    @Test("routeMapSize: has reasonable dimensions")
    func routeMapSize_reasonable() {
        #expect(ShareCardDimensions.routeMapSize.width == 960)
        #expect(ShareCardDimensions.routeMapSize.height == 576)
    }

    /// - Expected Behavior: Padding value is positive
    @Test("padding: is positive")
    func padding_positive() {
        #expect(ShareCardDimensions.padding == 60)
        #expect(ShareCardDimensions.padding > 0)
    }

    /// - Expected Behavior: Header height is positive
    @Test("headerHeight: is positive")
    func headerHeight_positive() {
        #expect(ShareCardDimensions.headerHeight == 120)
        #expect(ShareCardDimensions.headerHeight > 0)
    }

    /// - Expected Behavior: Footer height is positive
    @Test("footerHeight: is positive")
    func footerHeight_positive() {
        #expect(ShareCardDimensions.footerHeight == 100)
        #expect(ShareCardDimensions.footerHeight > 0)
    }

    // MARK: - Invariant Tests

    /// - Invariant: Card aspect ratio is 9:16 (portrait)
    @Test("Invariant: card is portrait aspect ratio")
    func invariant_portraitAspectRatio() {
        let aspectRatio = ShareCardDimensions.cardSize.width / ShareCardDimensions.cardSize.height
        // 9:16 = 0.5625
        #expect(abs(aspectRatio - 0.5625) < 0.001)
    }

    /// - Invariant: Map sizes fit within card with padding
    @Test("Invariant: maps fit within card")
    func invariant_mapsFitWithinCard() {
        let availableWidth = ShareCardDimensions.cardSize.width - (ShareCardDimensions.padding * 2)

        #expect(ShareCardDimensions.mapSnapshotSize.width <= availableWidth)
        #expect(ShareCardDimensions.routeMapSize.width <= availableWidth)
    }
}
