//
//  StadiumProgress.swift
//  SportsTime
//
//  SwiftData models for tracking stadium visits, achievements, and photo metadata.
//

import Foundation
import SwiftData

// MARK: - Visit Type

enum VisitType: String, Codable, CaseIterable {
    case game   // Attended a game
    case tour   // Stadium tour without game
    case other  // Other visit (tailgating, event, etc.)

    var displayName: String {
        switch self {
        case .game: return "Game"
        case .tour: return "Tour"
        case .other: return "Other"
        }
    }
}

// MARK: - Data Source Type

enum VisitDataSource: String, Codable {
    case automatic      // All data from photo + API
    case partialManual  // Photo metadata + manual game selection
    case fullyManual    // User entered everything
    case userCorrected  // Was automatic, user edited
}

// MARK: - Score Source Type

enum ScoreSource: String, Codable {
    case app     // From app's schedule data
    case api     // From free sports API
    case scraped // From reference site scraping
    case user    // User-provided
}

// MARK: - Visit Source Type

enum VisitSource: String, Codable {
    case trip        // Created from a planned trip
    case manual      // Manually entered
    case photoImport // Imported from photo library
}

// MARK: - Upload Status

enum UploadStatus: String, Codable {
    case pending
    case uploaded
    case failed
}

// MARK: - Stadium Visit

@Model
final class StadiumVisit {
    @Attribute(.unique) var id: UUID

    // Stadium identity (stable across renames)
    var stadiumId: String             // Canonical ID: "stadium_mlb_fenway_park"
    var stadiumNameAtVisit: String    // Frozen at visit time

    // Visit details
    var visitDate: Date
    var sport: String                 // Sport.rawValue
    var visitTypeRaw: String          // VisitType.rawValue

    // Game info (optional - nil for tours/other visits)
    var gameId: String?               // Canonical ID: "game_mlb_2026_bos_nyy_0401"
    var homeTeamId: String?           // Canonical ID: "team_mlb_bos"
    var awayTeamId: String?           // Canonical ID: "team_mlb_nyy"
    var homeTeamName: String?         // For display when team lookup fails
    var awayTeamName: String?
    var finalScore: String?           // "5-3" format
    var manualGameDescription: String? // User's description if game not found

    // Resolution tracking
    var scoreSourceRaw: String?       // ScoreSource.rawValue
    var dataSourceRaw: String         // VisitDataSource.rawValue
    var scoreResolutionPending: Bool  // true if background retry needed

    // User data
    var seatLocation: String?
    var notes: String?

    // Photos
    @Relationship(deleteRule: .cascade, inverse: \VisitPhotoMetadata.visit)
    var photoMetadata: [VisitPhotoMetadata]?

    // Photo import metadata (preserved for debugging/re-matching)
    var photoLatitude: Double?
    var photoLongitude: Double?
    var photoCaptureDate: Date?

    // Audit
    var createdAt: Date
    var sourceRaw: String             // VisitSource.rawValue

    // MARK: - Initialization

    init(
        id: UUID = UUID(),
        stadiumId: String,
        stadiumNameAtVisit: String,
        visitDate: Date,
        sport: Sport,
        visitType: VisitType = .game,
        gameId: String? = nil,
        homeTeamId: String? = nil,
        awayTeamId: String? = nil,
        homeTeamName: String? = nil,
        awayTeamName: String? = nil,
        finalScore: String? = nil,
        manualGameDescription: String? = nil,
        scoreSource: ScoreSource? = nil,
        dataSource: VisitDataSource = .fullyManual,
        scoreResolutionPending: Bool = false,
        seatLocation: String? = nil,
        notes: String? = nil,
        photoLatitude: Double? = nil,
        photoLongitude: Double? = nil,
        photoCaptureDate: Date? = nil,
        source: VisitSource = .manual
    ) {
        self.id = id
        self.stadiumId = stadiumId
        self.stadiumNameAtVisit = stadiumNameAtVisit
        self.visitDate = visitDate
        self.sport = sport.rawValue
        self.visitTypeRaw = visitType.rawValue
        self.gameId = gameId
        self.homeTeamId = homeTeamId
        self.awayTeamId = awayTeamId
        self.homeTeamName = homeTeamName
        self.awayTeamName = awayTeamName
        self.finalScore = finalScore
        self.manualGameDescription = manualGameDescription
        self.scoreSourceRaw = scoreSource?.rawValue
        self.dataSourceRaw = dataSource.rawValue
        self.scoreResolutionPending = scoreResolutionPending
        self.seatLocation = seatLocation
        self.notes = notes
        self.photoLatitude = photoLatitude
        self.photoLongitude = photoLongitude
        self.photoCaptureDate = photoCaptureDate
        self.createdAt = Date()
        self.sourceRaw = source.rawValue
    }

    // MARK: - Computed Properties

    var visitType: VisitType {
        get { VisitType(rawValue: visitTypeRaw) ?? .game }
        set { visitTypeRaw = newValue.rawValue }
    }

    var scoreSource: ScoreSource? {
        get { scoreSourceRaw.flatMap { ScoreSource(rawValue: $0) } }
        set { scoreSourceRaw = newValue?.rawValue }
    }

    var dataSource: VisitDataSource {
        get { VisitDataSource(rawValue: dataSourceRaw) ?? .fullyManual }
        set { dataSourceRaw = newValue.rawValue }
    }

    var source: VisitSource {
        get { VisitSource(rawValue: sourceRaw) ?? .manual }
        set { sourceRaw = newValue.rawValue }
    }

    var sportEnum: Sport? {
        Sport(rawValue: sport)
    }

    /// Display string for the game matchup
    var matchupDescription: String? {
        if let home = homeTeamName, let away = awayTeamName {
            return "\(away) @ \(home)"
        }
        return manualGameDescription
    }

    /// Display string including score if available
    var matchupWithScore: String? {
        guard let matchup = matchupDescription else { return nil }
        if let score = finalScore {
            return "\(matchup) (\(score))"
        }
        return matchup
    }
}

// MARK: - Visit Photo Metadata

@Model
final class VisitPhotoMetadata {
    @Attribute(.unique) var id: UUID
    var visitId: UUID
    var cloudKitAssetId: String?      // Set after successful upload
    var thumbnailData: Data?          // 200x200 JPEG for fast loading
    var caption: String?
    var orderIndex: Int
    var uploadStatusRaw: String       // UploadStatus.rawValue
    var createdAt: Date

    var visit: StadiumVisit?

    init(
        id: UUID = UUID(),
        visitId: UUID,
        cloudKitAssetId: String? = nil,
        thumbnailData: Data? = nil,
        caption: String? = nil,
        orderIndex: Int = 0,
        uploadStatus: UploadStatus = .pending
    ) {
        self.id = id
        self.visitId = visitId
        self.cloudKitAssetId = cloudKitAssetId
        self.thumbnailData = thumbnailData
        self.caption = caption
        self.orderIndex = orderIndex
        self.uploadStatusRaw = uploadStatus.rawValue
        self.createdAt = Date()
    }

    var uploadStatus: UploadStatus {
        get { UploadStatus(rawValue: uploadStatusRaw) ?? .pending }
        set { uploadStatusRaw = newValue.rawValue }
    }
}

// MARK: - Achievement

@Model
final class Achievement {
    @Attribute(.unique) var id: UUID
    var achievementTypeId: String     // e.g., "mlb_all_30", "nl_west_complete"
    var sport: String?                // Sport.rawValue, nil for cross-sport achievements
    var earnedAt: Date
    var revokedAt: Date?              // Non-nil if visits deleted
    var visitIdsSnapshot: Data        // JSON-encoded [UUID] that earned this

    init(
        id: UUID = UUID(),
        achievementTypeId: String,
        sport: Sport? = nil,
        earnedAt: Date = Date(),
        visitIds: [UUID]
    ) {
        self.id = id
        self.achievementTypeId = achievementTypeId
        self.sport = sport?.rawValue
        self.earnedAt = earnedAt
        self.revokedAt = nil
        self.visitIdsSnapshot = (try? JSONEncoder().encode(visitIds)) ?? Data()
    }

    var sportEnum: Sport? {
        sport.flatMap { Sport(rawValue: $0) }
    }

    var visitIds: [UUID] {
        (try? JSONDecoder().decode([UUID].self, from: visitIdsSnapshot)) ?? []
    }

    var isEarned: Bool {
        revokedAt == nil
    }

    func revoke() {
        revokedAt = Date()
    }

    func restore() {
        revokedAt = nil
    }
}

// MARK: - Cached Game Score

/// Caches resolved game scores to avoid repeated API calls.
/// Historical scores never change, so they can be cached indefinitely.
@Model
final class CachedGameScore {
    @Attribute(.unique) var cacheKey: String  // "MLB_2010-06-15_SFG_LAD"
    var sport: String
    var gameDate: Date
    var homeTeamAbbrev: String
    var awayTeamAbbrev: String
    var homeTeamName: String
    var awayTeamName: String
    var homeScore: Int?
    var awayScore: Int?
    var sourceRaw: String             // ScoreSource.rawValue
    var fetchedAt: Date
    var expiresAt: Date?              // nil = never expires (historical data)

    init(
        cacheKey: String,
        sport: Sport,
        gameDate: Date,
        homeTeamAbbrev: String,
        awayTeamAbbrev: String,
        homeTeamName: String,
        awayTeamName: String,
        homeScore: Int?,
        awayScore: Int?,
        source: ScoreSource,
        expiresAt: Date? = nil
    ) {
        self.cacheKey = cacheKey
        self.sport = sport.rawValue
        self.gameDate = gameDate
        self.homeTeamAbbrev = homeTeamAbbrev
        self.awayTeamAbbrev = awayTeamAbbrev
        self.homeTeamName = homeTeamName
        self.awayTeamName = awayTeamName
        self.homeScore = homeScore
        self.awayScore = awayScore
        self.sourceRaw = source.rawValue
        self.fetchedAt = Date()
        self.expiresAt = expiresAt
    }

    var scoreSource: ScoreSource {
        get { ScoreSource(rawValue: sourceRaw) ?? .api }
        set { sourceRaw = newValue.rawValue }
    }

    var sportEnum: Sport? {
        Sport(rawValue: sport)
    }

    var scoreString: String? {
        guard let home = homeScore, let away = awayScore else { return nil }
        return "\(away)-\(home)"
    }

    var isExpired: Bool {
        guard let expiresAt = expiresAt else { return false }
        return Date() > expiresAt
    }

    /// Generate cache key for a game query
    static func generateKey(sport: Sport, date: Date, homeAbbrev: String, awayAbbrev: String) -> String {
        let dateFormatter = DateFormatter()
        dateFormatter.dateFormat = "yyyy-MM-dd"
        let dateString = dateFormatter.string(from: date)
        return "\(sport.rawValue)_\(dateString)_\(homeAbbrev)_\(awayAbbrev)"
    }
}
