import Foundation

public extension String {
    static let onboardingFlowStartedSent = "onboardingFlowStartedSent"
    static let onboardingFlowEndedSent = "onboardingFlowEndedSent"

    static let playingClipKey = "playingClipKey"
    static let isPlayingKey = "isPlayingKey"
    static let playingVideoCoverUrlKey = "playingVideoCoverUrlKey"
    static let readyNewGensCount = "readyNewGensCount"
    static let likeCount = "likeCount"
    static let shareCount = "shareCount"
    static let addedToPlaylist = "addedToPlaylist"
    static let songCreateCount = "songCreateCount"
    static let hasRated = "hasRated"
    static let lastRatingRequestSeen = "lastRatingRequestSeen"
    static let contactSyncSeen = "contactSyncSeen"
    static let commentsAccessMap = "commentsAccessMap"
    static let commentsCountMap = "commentsCountMap"
    static let commentsActivationState = "commentsActivationState"
    static let commentsSheetAccessMap = "commentsSheetAccessMap"
    static let commentsSheetCountMap = "commentsSheetCountMap"
    static let commentsSheetActivationState = "commentsSheetActivationState"
    static let showInviteFriends = "showInviteFriends"
    static let dismissedInviteFriendsDiscover = "dismissedInviteFriendsDiscover"
    static let dismissedContactSyncCreatorsToFollow = "dismissedContactSyncCreatorsToFollow"
    static let dismissedContactSyncDiscover = "dismissedContactSyncDiscover"
    static let dismissedInternationalInvitesDiscover = "dismissedInternationalInvitesDiscover"
    static let tappedAddNumberButtonInProfile = "tappedAddNumberButtonInProfile"
    // Last creation mode: text, camera, audio
    static let lastCreateMode = "lastCreateMode"
    // Last camera mode: photo or video
    static let lastCameraFlashToggleMode = "lastCameraFlashToggleMode"
    static let hasSeenHowToUseAudio = "hasSeenHowToUseAudio"
    static let hasSeenContextPrompt = "hasSeenContextPrompt"
    static let hasSeenFtuxTwoClips = "hasSeenFtuxTwoClips"
    static let hasSeenCellularSceneUploadsAlert = "hasSeenCellularSceneUploadsAlert"
    static let dismissedFeaturedArtistDiscover = "dismissedFeaturedArtistDiscover"
    static let hasSeenFeaturedArtist = "hasSeenFeaturedArtist"
    static let hasSeenInternationalInvitesAlert = "hasSeenInternationalInvitesAlert"
    static let promoCodeUrl = "promoCodeUrl"
    static let promoCodeUrlRedemptionsLeft = "promoCodeUrlRedemptionsLeft"
    static let hasSeenCreateFirstSong = "hasSeenCreateFirstSong"
    static let hasSeenRemasterTooltip = "hasSeenRemasterTooltip"
    static let hasSeenOmniPlayerTooltipThisSession = "hasSeenOmniPlayerTooltipThisSession"
    static let dismissedAnnouncements = "dismissedAnnouncements"
    static let isCompactPlayerVisible = "isCompactPlayerVisible"
    static let isHooksOmniPlayerVisible = "isHooksOmniPlayerVisible"
    static let isHooksCreateVisible = "isHooksCreateVisible"
    /* Lyrics */
    static let alignedLyricsMap = "alignedLyricsMap"

    /* Appearance */
    static let appearanceMode = "appearanceMode"

    /* Notifications - in memory app state items that drive UI */
    static let inAppNotificationMap = "inAppNotificationMap"
    static let hasUnreadNotifications = "hasUnreadNotifications"
    static let inAppNotificationsState = "inAppNotificationsState"
    static let hasDismissedPushNotificationBanner = "hasDismissedPushNotificationBanner"

    /* Notifications - in app storage data that persists to be used along side requests */
    /* static let lastNotificationsNotifiedAt = "lastNotificationsNotifiedAt" ** Deprecated */
    static let lastNotificationNotifiedAtV2 = "lastNotificationNotifiedAtV2"
    /* Notifications - in-memory storage as an interim solution in case app-storage isn't working */
    static let lastNotificationNotifiedAtInMemory = "lastNotificationNotifiedAtInMemory"
    /* Notifications - banner presentation*/
    static let dismissedNotificationsBannerId = "dismissedNotificationsBannerId"

    /* Suno Generation Model */
    static let selectedSunoModel = "selectedSunoModel"
    static let selectedSunoRemasterModel = "selectedSunoRemasterModel"
    static let sunoModelUserAccessCategory = "sunoModelUserAccessCategory"
    static let defaultSunoModelForUserAccessCategory = "defaultSunoModelForUserAccessCategory"
    static let defaultSunoRemasterModelForUserAccessCategory = "defaultSunoRemasterModelForUserAccessCategory"
    static let availableModels = "availableModels"
    static let currentUserModels = "currentUserModels"

    /* Share Asset */
    static let shareAssetGenerationMap = "shareAssetGenerationMap"
    static let platformsLRUOrder = "platformsLRUOrder"
    static let isShareAssetCreationMuted = "isShareAssetCreationMuted"

    // A dictionary keyed by user ID for what model version to use
    static let modelVersioningOverride = "modelVersioningOverride"
    static let remasterModelVersioningOverride = "remasterModelVersioningOverride"

    static let lastDiscoverUpdateAt = "lastDiscoverUpdateAt"

    static let hasSeenTapToMakeMoreExtensionsTooltip = "hasSeenTapToMakeMoreExtensionsTooltip"
    static let hasSeenExtendOmniPlayerTooltip = "hasSeenExtendOmniPlayerTooltip"
    static let hasSeenNewSongsOmniPlayerTooltip = "hasSeenNewSongsOmniPlayerTooltip"

    static let analyticsSessionId = "analyticsSessionId"

    // Profile
    static let isCreatorsToFollowVisible = "isCreatorsToFollowVisible"

    // Selecting extend or cover style in audio create. Remembers selection
    static let audioCreateStyle = "audioCreateStyle"
    static let hasSeenAudioCreateStyleTooltip = "hasSeenAudioCreateStyleTooltip"

    // Billing/plan
    static let billingInfo = "billingInfo"

    // Captions
    static let hasUsedCaptionsFeature = "hasUsedCaptionsFeature"
    static let clipsTippedToCaption = "clipsTippedToCaption"

    // Video covers
    static let hasUsedVideoCoversFeature = "hasUsedVideoCoversFeature"
    static let hasSeenVideoSongArtWalkthroughAlert = "hasSeenVideoSongArtWalkthroughAlert"

    // Blended create
    static let lastGenerationType = "lastGenerationType"

    // Remix
    static let clipParentMap = "clipParentMap"
    static let clipDirectChildrenCount = "clipDirectChildrenCount"
    static let clipRemixabilityMap = "clipRemixabilityMap"

    // OmniPlayer
    static let omniPlayerDragOffset = "omniPlayerDragOffset"

    // Startup Orchestrator
    static let startUpOrchestratorEnabled = "startUpOrchestratorEnabledKey"

    // Environment overrides
    static let selectedBackendEnvironment = "selectedBackendEnvironment"

    // Haptics
    static let hapticsEnabled = "hapticsEnabled"

    // V5
    static let hasSeenV5Announcement = "hasSeenV5Announcement"

    // Hooks
    static let hasSeenHooksOnboarding = "hasSeenHooksOnboarding"
    static let hasSeenHooksCaptureAdjustSongTooltip = "hasSeenHooksCaptureAdjustSongTooltip"
    static let hasSeenHooksCaptureAdjustAudioTooltip = "hasSeenHooksCaptureAdjustAudioTooltip"

    // Tab bar
    static let selectedTab = "selectedTab"
    static let isOnHooksFeed = "isOnHooksFeed"
    static let isHooksFeedFocused = "isHooksFeedFocused"
    static let hasSeenFirstTimeTabSelection = "hasSeenFirstTimeTabSelection"
    static let overrideColorScheme = "overrideColorScheme"

    // Statsig
    static let lastStatsigUserId = "lastStatsigUserId"
    static let lastStatsigUserUSState = "lastStatsigUserUSState"

    // Deprecate Scenes Banner
    static let hasSeenDeprecateScenesBanner = "hasSeenDeprecateScenesBanner"
}

// Allows storing dates in @Shared(.appStorage(...))
extension Date: @retroactive RawRepresentable {
    public var rawValue: String {
        self.timeIntervalSinceReferenceDate.description
    }

    public init?(rawValue: String) {
        self = Date(timeIntervalSinceReferenceDate: Double(rawValue) ?? 0.0)
    }
}

// Allows storing arrays in @Shared(.appStorage(...))
extension Array: @retroactive RawRepresentable where Element: Codable {
    public init?(rawValue: String) {
        guard let data = rawValue.data(using: .utf8),
              let result = try? JSONDecoder().decode([Element].self, from: data) else { return nil }
        self = result
    }

    public var rawValue: String {
        guard let data = try? JSONEncoder().encode(self),
              let result = String(data: data, encoding: .utf8) else { return "[]" }
        return result
    }
}
