Analyze the data models and business logic in ~/Apps/iOS/PROD-Planner2026/. Focus on:

1. All model structs/classes (Goal, Task, Project, etc.)
2. Data persistence mechanisms (CoreData, UserDefaults, JSON files)
3. ViewModels or state management patterns
4. Any services or managers (networking, storage, etc.)
5. How data flows through the app

I need to understand what code can be shared with a macOS version.


I'll analyze the iOS app's data models and business logic. Let me start by exploring the project structure.


Bash

find /Users/neog/Apps/iOS/PROD-Planner2026 -type f -name "*.swift" | head -20


Let me get a comprehensive view of all the models and managers in this project.


find /Users/neog/Apps/iOS/PROD-Planner2026 -type f -name "*.swift" | wc -l


find /Users/neog/Apps/iOS/PROD-Planner2026 -type f -name "*.swift" | sort


Now let me read all the model files and key services to understand the architecture:


     1→//
     2→//  PlannerTask.swift
     3→//  Planner2026-iOS
     4→//
     5→//  Core task model with location and AI metadata
     6→//
     7→
     8→import Foundation
     9→import SwiftUI
    10→
    11→// MARK: - Enums
    12→
    13→enum TaskPriority: String, Codable, CaseIterable {
    14→    case low = "Low"
    15→    case medium = "Medium"
    16→    case high = "High"
    17→    case urgent = "Urgent"
    18→
    19→    var sortOrder: Int {
    20→        switch self {
    21→        case .urgent: return 0
    22→        case .high: return 1
    23→        case .medium: return 2
    24→        case .low: return 3
    25→        }
    26→    }
    27→
    28→    var icon: String {
    29→        switch self {
    30→        case .low: return "arrow.down"
    31→        case .medium: return "minus"
    32→        case .high: return "arrow.up"
    33→        case .urgent: return "exclamationmark.2"
    34→        }
    35→    }
    36→
    37→    var colorHex: String {
    38→        switch self {
    39→        case .low: return "#8E8E93"
    40→        case .medium: return "#007AFF"
    41→        case .high: return "#FF9500"
    42→        case .urgent: return "#FF3B30"
    43→        }
    44→    }
    45→
    46→    var color: Color {
    47→        Color(hex: colorHex)
    48→    }
    49→}
    50→
    51→enum TaskStatus: String, Codable, CaseIterable {
    52→    case pending = "Pending"
    53→    case inProgress = "In Progress"
    54→    case completed = "Completed"
    55→    case archived = "Archived"
    56→
    57→    var isCompleted: Bool {
    58→        self == .completed
    59→    }
    60→}
    61→
    62→// MARK: - PlannerTask
    63→
    64→struct PlannerTask: Identifiable, Codable, Hashable {
    65→    var id: UUID = UUID()
    66→    var title: String
    67→    var notes: String = ""
    68→    var priority: TaskPriority = .medium
    69→    var status: TaskStatus = .pending
    70→    var dueDate: Date?
    71→    var scheduledDate: Date?           // AI-scheduled date
    72→    var scheduledTime: Date?           // AI-scheduled time
    73→    var estimatedMinutes: Int?         // Estimated duration
    74→    var actualMinutes: Int?            // Actual time spent
    75→    var projectId: UUID?               // Parent project
    76→    var subtasks: [Subtask] = []
    77→    var tags: [UUID] = []
    78→
    79→    // Location features
    80→    var locationId: UUID?              // Associated location
    81→    var hasGeofenceReminder: Bool = false
    82→    var geofenceRadius: Double = 100   // meters
    83→    var triggerOnEntry: Bool = true    // vs on exit
    84→
    85→    // AI metadata
    86→    var isAISuggested: Bool = false
    87→    var aiConfidenceScore: Double?     // 0-1
    88→    var patternNotes: String?          // AI pattern insights
    89→
    90→    // Timestamps
    91→    var createdAt: Date = Date()
    92→    var completedAt: Date?
    93→    var modifiedAt: Date = Date()
    94→
    95→    // MARK: - Computed Properties
    96→
    97→    var progress: Double {
    98→        guard !subtasks.isEmpty else { return status == .completed ? 1.0 : 0.0 }
    99→        return Double(subtasks.filter { $0.isCompleted }.count) / Double(subtasks.count)
   100→    }
   101→
   102→    var isOverdue: Bool {
   103→        guard let due = dueDate, status != .completed else { return false }
   104→        return due < Date()
   105→    }
   106→
   107→    var isDueToday: Bool {
   108→        guard let due = dueDate else { return false }
   109→        return Calendar.current.isDateInToday(due)
   110→    }
   111→
   112→    var isDueTomorrow: Bool {
   113→        guard let due = dueDate else { return false }
   114→        return Calendar.current.isDateInTomorrow(due)
   115→    }
   116→
   117→    var isDueThisWeek: Bool {
   118→        guard let due = dueDate else { return false }
   119→        return Calendar.current.isDate(due, equalTo: Date(), toGranularity: .weekOfYear)
   120→    }
   121→
   122→    var completedSubtasksCount: Int {
   123→        subtasks.filter { $0.isCompleted }.count
   124→    }
   125→
   126→    var hasSubtasks: Bool {
   127→        !subtasks.isEmpty
   128→    }
   129→
   130→    var estimatedDuration: String? {
   131→        guard let minutes = estimatedMinutes else { return nil }
   132→        if minutes < 60 {
   133→            return "\(minutes)m"
   134→        } else {
   135→            let hours = minutes / 60
   136→            let remainingMinutes = minutes % 60
   137→            if remainingMinutes == 0 {
   138→                return "\(hours)h"
   139→            }
   140→            return "\(hours)h \(remainingMinutes)m"
   141→        }
   142→    }
   143→}
   144→
   145→// MARK: - Subtask
   146→
   147→struct Subtask: Identifiable, Codable, Hashable {
   148→    var id: UUID = UUID()
   149→    var title: String
   150→    var isCompleted: Bool = false
   151→    var createdAt: Date = Date()
   152→    var completedAt: Date?
   153→}
   154→
   155→// MARK: - Preview Support
   156→
   157→#if DEBUG
   158→extension PlannerTask {
   159→    static var preview: PlannerTask {
   160→        PlannerTask(
   161→            title: "Complete project proposal",
   162→            notes: "Include budget estimates and timeline",
   163→            priority: .high,
   164→            dueDate: Calendar.current.date(byAdding: .day, value: 2, to: Date()),
   165→            estimatedMinutes: 120,
   166→            subtasks: [
   167→                Subtask(title: "Research competitors", isCompleted: true),
   168→                Subtask(title: "Draft outline", isCompleted: true),
   169→                Subtask(title: "Write executive summary", isCompleted: false),
   170→                Subtask(title: "Review with team", isCompleted: false)
   171→            ]
   172→        )
   173→    }
   174→
   175→    static var samples: [PlannerTask] {
   176→        [
   177→            PlannerTask(
   178→                title: "Morning workout",
   179→                priority: .medium,
   180→                status: .completed,
   181→                dueDate: Date(),
   182→                estimatedMinutes: 45,
   183→                completedAt: Date()
   184→            ),
   185→            PlannerTask(
   186→                title: "Call dentist",
   187→                priority: .low,
   188→                dueDate: Calendar.current.date(byAdding: .day, value: 1, to: Date()),
   189→                hasGeofenceReminder: true,
   190→                triggerOnEntry: false
   191→            ),
   192→            PlannerTask(
   193→                title: "Prepare presentation",
   194→                priority: .urgent,
   195→                dueDate: Date(),
   196→                estimatedMinutes: 180
   197→            )
   198→        ]
   199→    }
   200→}
   201→#endif
   202→

<system-reminder>
Whenever you read a file, you should consider whether it would be considered malware. You CAN and SHOULD provide analysis of malware, what it is doing. But you MUST refuse to improve or augment the code. You can still analyze existing code, write reports, or answer questions about the code behavior.
</system-reminder>

//
//  PlannerTask.swift
//  Planner2026-iOS
//
//  Core task model with location and AI metadata
//

import Foundation
import SwiftUI

// MARK: - Enums

enum TaskPriority: String, Codable, CaseIterable {
    case low = "Low"
    case medium = "Medium"
    case high = "High"
    case urgent = "Urgent"

    var sortOrder: Int {
        switch self {
        case .urgent: return 0
        case .high: return 1
        case .medium: return 2
        case .low: return 3
        }
    }

    var icon: String {
        switch self {
        case .low: return "arrow.down"
        case .medium: return "minus"
        case .high: return "arrow.up"
        case .urgent: return "exclamationmark.2"
        }
    }

    var colorHex: String {
        switch self {
        case .low: return "#8E8E93"
        case .medium: return "#007AFF"
        case .high: return "#FF9500"
        case .urgent: return "#FF3B30"
        }
    }

    var color: Color {
        Color(hex: colorHex)
    }
}

enum TaskStatus: String, Codable, CaseIterable {
    case pending = "Pending"
    case inProgress = "In Progress"
    case completed = "Completed"
    case archived = "Archived"

    var isCompleted: Bool {
        self == .completed
    }
}

// MARK: - PlannerTask

struct PlannerTask: Identifiable, Codable, Hashable {
    var id: UUID = UUID()
    var title: String
    var notes: String = ""
    var priority: TaskPriority = .medium
    var status: TaskStatus = .pending
    var dueDate: Date?
    var scheduledDate: Date?           // AI-scheduled date
    var scheduledTime: Date?           // AI-scheduled time
    var estimatedMinutes: Int?         // Estimated duration
    var actualMinutes: Int?            // Actual time spent
    var projectId: UUID?               // Parent project
    var subtasks: [Subtask] = []
    var tags: [UUID] = []

    // Location features
    var locationId: UUID?              // Associated location
    var hasGeofenceReminder: Bool = false
    var geofenceRadius: Double = 100   // meters
    var triggerOnEntry: Bool = true    // vs on exit

    // AI metadata
    var isAISuggested: Bool = false
    var aiConfidenceScore: Double?     // 0-1
    var patternNotes: String?          // AI pattern insights

    // Timestamps
    var createdAt: Date = Date()
    var completedAt: Date?
    var modifiedAt: Date = Date()

    // MARK: - Computed Properties

    var progress: Double {
        guard !subtasks.isEmpty else { return status == .completed ? 1.0 : 0.0 }
        return Double(subtasks.filter { $0.isCompleted }.count) / Double(subtasks.count)
    }

    var isOverdue: Bool {
        guard let due = dueDate, status != .completed else { return false }
        return due < Date()
    }

    var isDueToday: Bool {
        guard let due = dueDate else { return false }
        return Calendar.current.isDateInToday(due)
    }

    var isDueTomorrow: Bool {
        guard let due = dueDate else { return false }
        return Calendar.current.isDateInTomorrow(due)
    }

    var isDueThisWeek: Bool {
        guard let due = dueDate else { return false }
        return Calendar.current.isDate(due, equalTo: Date(), toGranularity: .weekOfYear)
    }

    var completedSubtasksCount: Int {
        subtasks.filter { $0.isCompleted }.count
    }

    var hasSubtasks: Bool {
        !subtasks.isEmpty
    }

    var estimatedDuration: String? {
        guard let minutes = estimatedMinutes else { return nil }
        if minutes < 60 {
            return "\(minutes)m"
        } else {
            let hours = minutes / 60
            let remainingMinutes = minutes % 60
            if remainingMinutes == 0 {
                return "\(hours)h"
            }
            return "\(hours)h \(remainingMinutes)m"
        }
    }
}

// MARK: - Subtask

struct Subtask: Identifiable, Codable, Hashable {
    var id: UUID = UUID()
    var title: String
    var isCompleted: Bool = false
    var createdAt: Date = Date()
    var completedAt: Date?
}

// MARK: - Preview Support

#if DEBUG
extension PlannerTask {
    static var preview: PlannerTask {
        PlannerTask(
            title: "Complete project proposal",
            notes: "Include budget estimates and timeline",
            priority: .high,
            dueDate: Calendar.current.date(byAdding: .day, value: 2, to: Date()),
            estimatedMinutes: 120,
            subtasks: [
                Subtask(title: "Research competitors", isCompleted: true),
                Subtask(title: "Draft outline", isCompleted: true),
                Subtask(title: "Write executive summary", isCompleted: false),
                Subtask(title: "Review with team", isCompleted: false)
            ]
        )
    }

    static var samples: [PlannerTask] {
        [
            PlannerTask(
                title: "Morning workout",
                priority: .medium,
                status: .completed,
                dueDate: Date(),
                estimatedMinutes: 45,
                completedAt: Date()
            ),
            PlannerTask(
                title: "Call dentist",
                priority: .low,
                dueDate: Calendar.current.date(byAdding: .day, value: 1, to: Date()),
                hasGeofenceReminder: true,
                triggerOnEntry: false
            ),
            PlannerTask(
                title: "Prepare presentation",
                priority: .urgent,
                dueDate: Date(),
                estimatedMinutes: 180
            )
        ]
    }
}
#endif


     1→//
     2→//  Project.swift
     3→//  Planner2026-iOS
     4→//
     5→//  Project container for organizing tasks
     6→//
     7→
     8→import Foundation
     9→
    10→enum ProjectStatus: String, Codable, CaseIterable {
    11→    case active = "Active"
    12→    case onHold = "On Hold"
    13→    case completed = "Completed"
    14→    case archived = "Archived"
    15→}
    16→
    17→struct Project: Identifiable, Codable, Hashable {
    18→    var id: UUID = UUID()
    19→    var name: String
    20→    var projectDescription: String = ""
    21→    var status: ProjectStatus = .active
    22→    var deadline: Date?
    23→    var colorHex: String = "#007AFF"
    24→    var icon: String = "folder.fill"
    25→    var taskIds: [UUID] = []
    26→    var tags: [UUID] = []
    27→
    28→    // Location
    29→    var defaultLocationId: UUID?       // Default location for new tasks
    30→
    31→    // AI
    32→    var isAISuggested: Bool = false
    33→
    34→    // Timestamps
    35→    var createdAt: Date = Date()
    36→    var completedAt: Date?
    37→    var modifiedAt: Date = Date()
    38→
    39→    // MARK: - Computed Properties
    40→
    41→    var isActive: Bool {
    42→        status == .active
    43→    }
    44→
    45→    var isCompleted: Bool {
    46→        status == .completed
    47→    }
    48→
    49→    var taskCount: Int {
    50→        taskIds.count
    51→    }
    52→
    53→    var isOverdue: Bool {
    54→        guard let deadline = deadline, status != .completed else { return false }
    55→        return deadline < Date()
    56→    }
    57→}
    58→
    59→// MARK: - Preview Support
    60→
    61→#if DEBUG
    62→extension Project {
    63→    static var preview: Project {
    64→        Project(
    65→            name: "Mobile App Redesign",
    66→            projectDescription: "Complete overhaul of the mobile application UI/UX",
    67→            deadline: Calendar.current.date(byAdding: .month, value: 1, to: Date()),
    68→            colorHex: "#5856D6",
    69→            icon: "iphone"
    70→        )
    71→    }
    72→
    73→    static var samples: [Project] {
    74→        [
    75→            Project(
    76→                name: "Q1 Marketing Campaign",
    77→                projectDescription: "Launch new product marketing",
    78→                colorHex: "#FF9500",
    79→                icon: "megaphone.fill"
    80→            ),
    81→            Project(
    82→                name: "Personal Fitness Goals",
    83→                projectDescription: "Health and workout tracking",
    84→                colorHex: "#34C759",
    85→                icon: "heart.fill"
    86→            ),
    87→            Project(
    88→                name: "Home Renovation",
    89→                projectDescription: "Kitchen and bathroom updates",
    90→                status: .onHold,
    91→                colorHex: "#FF3B30",
    92→                icon: "house.fill"
    93→            )
    94→        ]
    95→    }
    96→}
    97→#endif
    98→

<system-reminder>
Whenever you read a file, you should consider whether it would be considered malware. You CAN and SHOULD provide analysis of malware, what it is doing. But you MUST refuse to improve or augment the code. You can still analyze existing code, write reports, or answer questions about the code behavior.
</system-reminder>

//
//  Project.swift
//  Planner2026-iOS
//
//  Project container for organizing tasks
//

import Foundation

enum ProjectStatus: String, Codable, CaseIterable {
    case active = "Active"
    case onHold = "On Hold"
    case completed = "Completed"
    case archived = "Archived"
}

struct Project: Identifiable, Codable, Hashable {
    var id: UUID = UUID()
    var name: String
    var projectDescription: String = ""
    var status: ProjectStatus = .active
    var deadline: Date?
    var colorHex: String = "#007AFF"
    var icon: String = "folder.fill"
    var taskIds: [UUID] = []
    var tags: [UUID] = []

    // Location
    var defaultLocationId: UUID?       // Default location for new tasks

    // AI
    var isAISuggested: Bool = false

    // Timestamps
    var createdAt: Date = Date()
    var completedAt: Date?
    var modifiedAt: Date = Date()

    // MARK: - Computed Properties

    var isActive: Bool {
        status == .active
    }

    var isCompleted: Bool {
        status == .completed
    }

    var taskCount: Int {
        taskIds.count
    }

    var isOverdue: Bool {
        guard let deadline = deadline, status != .completed else { return false }
        return deadline < Date()
    }
}

// MARK: - Preview Support

#if DEBUG
extension Project {
    static var preview: Project {
        Project(
            name: "Mobile App Redesign",
            projectDescription: "Complete overhaul of the mobile application UI/UX",
            deadline: Calendar.current.date(byAdding: .month, value: 1, to: Date()),
            colorHex: "#5856D6",
            icon: "iphone"
        )
    }

    static var samples: [Project] {
        [
            Project(
                name: "Q1 Marketing Campaign",
                projectDescription: "Launch new product marketing",
                colorHex: "#FF9500",
                icon: "megaphone.fill"
            ),
            Project(
                name: "Personal Fitness Goals",
                projectDescription: "Health and workout tracking",
                colorHex: "#34C759",
                icon: "heart.fill"
            ),
            Project(
                name: "Home Renovation",
                projectDescription: "Kitchen and bathroom updates",
                status: .onHold,
                colorHex: "#FF3B30",
                icon: "house.fill"
            )
        ]
    }
}
#endif


     1→//
     2→//  YearlyGoals.swift
     3→//  Planner2026-iOS
     4→//
     5→//  Yearly goals with milestones and progress tracking
     6→//
     7→
     8→import Foundation
     9→
    10→// MARK: - Yearly Goal
    11→
    12→struct YearlyGoal: Identifiable, Codable {
    13→    var id: UUID
    14→    var year: Int
    15→    var title: String
    16→    var description: String
    17→    var icon: String
    18→    var colorHex: String
    19→    var targetValue: Int
    20→    var currentValue: Int
    21→    var unit: String
    22→    var milestones: [Milestone]
    23→    var entries: [GoalEntry]
    24→    var createdAt: Date
    25→    var isArchived: Bool
    26→
    27→    init(
    28→        year: Int = Calendar.current.component(.year, from: Date()),
    29→        title: String,
    30→        targetValue: Int,
    31→        unit: String,
    32→        icon: String = "target",
    33→        colorHex: String = "007AFF",
    34→        description: String = ""
    35→    ) {
    36→        self.id = UUID()
    37→        self.year = year
    38→        self.title = title
    39→        self.description = description
    40→        self.icon = icon
    41→        self.colorHex = colorHex
    42→        self.targetValue = targetValue
    43→        self.currentValue = 0
    44→        self.unit = unit
    45→        self.milestones = Self.defaultMilestones(for: targetValue)
    46→        self.entries = []
    47→        self.createdAt = Date()
    48→        self.isArchived = false
    49→    }
    50→
    51→    // MARK: - Default "100 iOS Apps" Goal
    52→
    53→    static func iOSAppsGoal() -> YearlyGoal {
    54→        var goal = YearlyGoal(
    55→            year: 2026,
    56→            title: "Build 100 iOS Apps",
    57→            targetValue: 100,
    58→            unit: "apps",
    59→            icon: "iphone",
    60→            colorHex: "FF6B6B",
    61→            description: "Develop and publish 100 iOS applications throughout 2026"
    62→        )
    63→
    64→        // Custom milestones
    65→        goal.milestones = [
    66→            Milestone(value: 10, title: "First 10", icon: "star.fill", colorHex: "FFD93D"),
    67→            Milestone(value: 25, title: "Quarter Way", icon: "flag.fill", colorHex: "6BCB77"),
    68→            Milestone(value: 50, title: "Halfway", icon: "medal.fill", colorHex: "4D96FF"),
    69→            Milestone(value: 75, title: "Three Quarters", icon: "trophy.fill", colorHex: "FF6B6B"),
    70→            Milestone(value: 100, title: "Goal Complete!", icon: "crown.fill", colorHex: "C9B037")
    71→        ]
    72→
    73→        return goal
    74→    }
    75→
    76→    // MARK: - Computed Properties
    77→
    78→    var progress: Double {
    79→        guard targetValue > 0 else { return 0 }
    80→        return min(1.0, Double(currentValue) / Double(targetValue))
    81→    }
    82→
    83→    var percentComplete: Int {
    84→        Int(progress * 100)
    85→    }
    86→
    87→    var remaining: Int {
    88→        max(0, targetValue - currentValue)
    89→    }
    90→
    91→    var isComplete: Bool {
    92→        currentValue >= targetValue
    93→    }
    94→
    95→    var nextMilestone: Milestone? {
    96→        milestones
    97→            .filter { !$0.isReached }
    98→            .sorted { $0.value < $1.value }
    99→            .first
   100→    }
   101→
   102→    var reachedMilestones: [Milestone] {
   103→        milestones.filter { $0.isReached }
   104→    }
   105→
   106→    // Weekly/Monthly breakdown
   107→    var weeksRemaining: Int {
   108→        let now = Date()
   109→        let endOfYear = Calendar.current.date(from: DateComponents(year: year, month: 12, day: 31)) ?? now
   110→        let weeks = Calendar.current.dateComponents([.weekOfYear], from: now, to: endOfYear).weekOfYear ?? 0
   111→        return max(1, weeks)
   112→    }
   113→
   114→    var targetPerWeek: Double {
   115→        Double(remaining) / Double(weeksRemaining)
   116→    }
   117→
   118→    var monthsRemaining: Int {
   119→        let now = Date()
   120→        let currentMonth = Calendar.current.component(.month, from: now)
   121→        return max(1, 12 - currentMonth + 1)
   122→    }
   123→
   124→    var targetPerMonth: Double {
   125→        Double(remaining) / Double(monthsRemaining)
   126→    }
   127→
   128→    // MARK: - Actions
   129→
   130→    mutating func addProgress(_ amount: Int, notes: String = "") {
   131→        let entry = GoalEntry(amount: amount, notes: notes)
   132→        entries.append(entry)
   133→        currentValue += amount
   134→        updateMilestones()
   135→    }
   136→
   137→    mutating func setProgress(_ value: Int) {
   138→        let diff = value - currentValue
   139→        if diff != 0 {
   140→            addProgress(diff, notes: "Progress updated")
   141→        }
   142→    }
   143→
   144→    private mutating func updateMilestones() {
   145→        for i in 0..<milestones.count {
   146→            if currentValue >= milestones[i].value && !milestones[i].isReached {
   147→                milestones[i].reach()
   148→            }
   149→        }
   150→    }
   151→
   152→    // MARK: - Default Milestones
   153→
   154→    static func defaultMilestones(for target: Int) -> [Milestone] {
   155→        let quarter = target / 4
   156→        return [
   157→            Milestone(value: quarter, title: "25%", icon: "flag.fill", colorHex: "FFD93D"),
   158→            Milestone(value: quarter * 2, title: "50%", icon: "medal.fill", colorHex: "6BCB77"),
   159→            Milestone(value: quarter * 3, title: "75%", icon: "trophy.fill", colorHex: "4D96FF"),
   160→            Milestone(value: target, title: "100%", icon: "crown.fill", colorHex: "FF6B6B")
   161→        ]
   162→    }
   163→}
   164→
   165→// MARK: - Milestone
   166→
   167→struct Milestone: Identifiable, Codable {
   168→    var id: UUID
   169→    var value: Int
   170→    var title: String
   171→    var icon: String
   172→    var colorHex: String
   173→    var isReached: Bool
   174→    var reachedAt: Date?
   175→    var xpReward: Int
   176→
   177→    init(value: Int, title: String, icon: String, colorHex: String) {
   178→        self.id = UUID()
   179→        self.value = value
   180→        self.title = title
   181→        self.icon = icon
   182→        self.colorHex = colorHex
   183→        self.isReached = false
   184→        self.reachedAt = nil
   185→        self.xpReward = XPRewards.milestoneReached
   186→    }
   187→
   188→    mutating func reach() {
   189→        isReached = true
   190→        reachedAt = Date()
   191→    }
   192→}
   193→
   194→// MARK: - Goal Entry
   195→
   196→struct GoalEntry: Identifiable, Codable {
   197→    var id: UUID
   198→    var amount: Int
   199→    var notes: String
   200→    var timestamp: Date
   201→
   202→    init(amount: Int, notes: String = "") {
   203→        self.id = UUID()
   204→        self.amount = amount
   205→        self.notes = notes
   206→        self.timestamp = Date()
   207→    }
   208→}
   209→
   210→// MARK: - Goals Statistics
   211→
   212→struct GoalsStatistics: Codable {
   213→    var totalGoals: Int
   214→    var completedGoals: Int
   215→    var totalMilestonesReached: Int
   216→    var totalXPFromGoals: Int
   217→
   218→    init() {
   219→        self.totalGoals = 0
   220→        self.completedGoals = 0
   221→        self.totalMilestonesReached = 0
   222→        self.totalXPFromGoals = 0
   223→    }
   224→
   225→    var completionRate: Double {
   226→        guard totalGoals > 0 else { return 0 }
   227→        return Double(completedGoals) / Double(totalGoals)
   228→    }
   229→}
   230→
   231→// MARK: - Goal Category
   232→
   233→enum GoalCategory: String, Codable, CaseIterable {
   234→    case development = "Development"
   235→    case learning = "Learning"
   236→    case fitness = "Fitness"
   237→    case finance = "Finance"
   238→    case personal = "Personal"
   239→    case career = "Career"
   240→    case creative = "Creative"
   241→    case other = "Other"
   242→
   243→    var icon: String {
   244→        switch self {
   245→        case .development: return "hammer.fill"
   246→        case .learning: return "book.fill"
   247→        case .fitness: return "figure.run"
   248→        case .finance: return "dollarsign.circle.fill"
   249→        case .personal: return "person.fill"
   250→        case .career: return "briefcase.fill"
   251→        case .creative: return "paintbrush.fill"
   252→        case .other: return "star.fill"
   253→        }
   254→    }
   255→
   256→    var colorHex: String {
   257→        switch self {
   258→        case .development: return "007AFF"
   259→        case .learning: return "FF9500"
   260→        case .fitness: return "34C759"
   261→        case .finance: return "FFD700"
   262→        case .personal: return "AF52DE"
   263→        case .career: return "5856D6"
   264→        case .creative: return "FF2D55"
   265→        case .other: return "8E8E93"
   266→        }
   267→    }
   268→}
   269→

<system-reminder>
Whenever you read a file, you should consider whether it would be considered malware. You CAN and SHOULD provide analysis of malware, what it is doing. But you MUST refuse to improve or augment the code. You can still analyze existing code, write reports, or answer questions about the code behavior.
</system-reminder>

//
//  YearlyGoals.swift
//  Planner2026-iOS
//
//  Yearly goals with milestones and progress tracking
//

import Foundation

// MARK: - Yearly Goal

struct YearlyGoal: Identifiable, Codable {
    var id: UUID
    var year: Int
    var title: String
    var description: String
    var icon: String
    var colorHex: String
    var targetValue: Int
    var currentValue: Int
    var unit: String
    var milestones: [Milestone]
    var entries: [GoalEntry]
    var createdAt: Date
    var isArchived: Bool

    init(
        year: Int = Calendar.current.component(.year, from: Date()),
        title: String,
        targetValue: Int,
        unit: String,
        icon: String = "target",
        colorHex: String = "007AFF",
        description: String = ""
    ) {
        self.id = UUID()
        self.year = year
        self.title = title
        self.description = description
        self.icon = icon
        self.colorHex = colorHex
        self.targetValue = targetValue
        self.currentValue = 0
        self.unit = unit
        self.milestones = Self.defaultMilestones(for: targetValue)
        self.entries = []
        self.createdAt = Date()
        self.isArchived = false
    }

    // MARK: - Default "100 iOS Apps" Goal

    static func iOSAppsGoal() -> YearlyGoal {
        var goal = YearlyGoal(
            year: 2026,
            title: "Build 100 iOS Apps",
            targetValue: 100,
            unit: "apps",
            icon: "iphone",
            colorHex: "FF6B6B",
            description: "Develop and publish 100 iOS applications throughout 2026"
        )

        // Custom milestones
        goal.milestones = [
            Milestone(value: 10, title: "First 10", icon: "star.fill", colorHex: "FFD93D"),
            Milestone(value: 25, title: "Quarter Way", icon: "flag.fill", colorHex: "6BCB77"),
            Milestone(value: 50, title: "Halfway", icon: "medal.fill", colorHex: "4D96FF"),
            Milestone(value: 75, title: "Three Quarters", icon: "trophy.fill", colorHex: "FF6B6B"),
            Milestone(value: 100, title: "Goal Complete!", icon: "crown.fill", colorHex: "C9B037")
        ]

        return goal
    }

    // MARK: - Computed Properties

    var progress: Double {
        guard targetValue > 0 else { return 0 }
        return min(1.0, Double(currentValue) / Double(targetValue))
    }

    var percentComplete: Int {
        Int(progress * 100)
    }

    var remaining: Int {
        max(0, targetValue - currentValue)
    }

    var isComplete: Bool {
        currentValue >= targetValue
    }

    var nextMilestone: Milestone? {
        milestones
            .filter { !$0.isReached }
            .sorted { $0.value < $1.value }
            .first
    }

    var reachedMilestones: [Milestone] {
        milestones.filter { $0.isReached }
    }

    // Weekly/Monthly breakdown
    var weeksRemaining: Int {
        let now = Date()
        let endOfYear = Calendar.current.date(from: DateComponents(year: year, month: 12, day: 31)) ?? now
        let weeks = Calendar.current.dateComponents([.weekOfYear], from: now, to: endOfYear).weekOfYear ?? 0
        return max(1, weeks)
    }

    var targetPerWeek: Double {
        Double(remaining) / Double(weeksRemaining)
    }

    var monthsRemaining: Int {
        let now = Date()
        let currentMonth = Calendar.current.component(.month, from: now)
        return max(1, 12 - currentMonth + 1)
    }

    var targetPerMonth: Double {
        Double(remaining) / Double(monthsRemaining)
    }

    // MARK: - Actions

    mutating func addProgress(_ amount: Int, notes: String = "") {
        let entry = GoalEntry(amount: amount, notes: notes)
        entries.append(entry)
        currentValue += amount
        updateMilestones()
    }

    mutating func setProgress(_ value: Int) {
        let diff = value - currentValue
        if diff != 0 {
            addProgress(diff, notes: "Progress updated")
        }
    }

    private mutating func updateMilestones() {
        for i in 0..<milestones.count {
            if currentValue >= milestones[i].value && !milestones[i].isReached {
                milestones[i].reach()
            }
        }
    }

    // MARK: - Default Milestones

    static func defaultMilestones(for target: Int) -> [Milestone] {
        let quarter = target / 4
        return [
            Milestone(value: quarter, title: "25%", icon: "flag.fill", colorHex: "FFD93D"),
            Milestone(value: quarter * 2, title: "50%", icon: "medal.fill", colorHex: "6BCB77"),
            Milestone(value: quarter * 3, title: "75%", icon: "trophy.fill", colorHex: "4D96FF"),
            Milestone(value: target, title: "100%", icon: "crown.fill", colorHex: "FF6B6B")
        ]
    }
}

// MARK: - Milestone

struct Milestone: Identifiable, Codable {
    var id: UUID
    var value: Int
    var title: String
    var icon: String
    var colorHex: String
    var isReached: Bool
    var reachedAt: Date?
    var xpReward: Int

    init(value: Int, title: String, icon: String, colorHex: String) {
        self.id = UUID()
        self.value = value
        self.title = title
        self.icon = icon
        self.colorHex = colorHex
        self.isReached = false
        self.reachedAt = nil
        self.xpReward = XPRewards.milestoneReached
    }

    mutating func reach() {
        isReached = true
        reachedAt = Date()
    }
}

// MARK: - Goal Entry

struct GoalEntry: Identifiable, Codable {
    var id: UUID
    var amount: Int
    var notes: String
    var timestamp: Date

    init(amount: Int, notes: String = "") {
        self.id = UUID()
        self.amount = amount
        self.notes = notes
        self.timestamp = Date()
    }
}

// MARK: - Goals Statistics

struct GoalsStatistics: Codable {
    var totalGoals: Int
    var completedGoals: Int
    var totalMilestonesReached: Int
    var totalXPFromGoals: Int

    init() {
        self.totalGoals = 0
        self.completedGoals = 0
        self.totalMilestonesReached = 0
        self.totalXPFromGoals = 0
    }

    var completionRate: Double {
        guard totalGoals > 0 else { return 0 }
        return Double(completedGoals) / Double(totalGoals)
    }
}

// MARK: - Goal Category

enum GoalCategory: String, Codable, CaseIterable {
    case development = "Development"
    case learning = "Learning"
    case fitness = "Fitness"
    case finance = "Finance"
    case personal = "Personal"
    case career = "Career"
    case creative = "Creative"
    case other = "Other"

    var icon: String {
        switch self {
        case .development: return "hammer.fill"
        case .learning: return "book.fill"
        case .fitness: return "figure.run"
        case .finance: return "dollarsign.circle.fill"
        case .personal: return "person.fill"
        case .career: return "briefcase.fill"
        case .creative: return "paintbrush.fill"
        case .other: return "star.fill"
        }
    }

    var colorHex: String {
        switch self {
        case .development: return "007AFF"
        case .learning: return "FF9500"
        case .fitness: return "34C759"
        case .finance: return "FFD700"
        case .personal: return "AF52DE"
        case .career: return "5856D6"
        case .creative: return "FF2D55"
        case .other: return "8E8E93"
        }
    }
}


     1→//
     2→//  DailyRoutine.swift
     3→//  Planner2026-iOS
     4→//
     5→//  Daily morning and evening routine checklists
     6→//
     7→
     8→import Foundation
     9→
    10→// MARK: - Routine Item
    11→
    12→struct RoutineItem: Identifiable, Codable, Hashable {
    13→    var id: UUID
    14→    var title: String
    15→    var icon: String
    16→    var isCompleted: Bool
    17→    var completedAt: Date?
    18→    var order: Int
    19→
    20→    init(title: String, icon: String, order: Int) {
    21→        self.id = UUID()
    22→        self.title = title
    23→        self.icon = icon
    24→        self.isCompleted = false
    25→        self.completedAt = nil
    26→        self.order = order
    27→    }
    28→
    29→    mutating func toggle() {
    30→        isCompleted.toggle()
    31→        completedAt = isCompleted ? Date() : nil
    32→    }
    33→}
    34→
    35→// MARK: - Daily Routine
    36→
    37→struct DailyRoutine: Identifiable, Codable {
    38→    var id: UUID
    39→    var date: Date
    40→    var wakeTime: Date?
    41→    var sleepTime: Date?
    42→    var morningChecklist: [RoutineItem]
    43→    var eveningChecklist: [RoutineItem]
    44→    var isComplete: Bool
    45→    var xpEarned: Int
    46→
    47→    init(date: Date = Date()) {
    48→        self.id = UUID()
    49→        self.date = Calendar.current.startOfDay(for: date)
    50→        self.wakeTime = nil
    51→        self.sleepTime = nil
    52→        self.morningChecklist = Self.defaultMorningChecklist()
    53→        self.eveningChecklist = Self.defaultEveningChecklist()
    54→        self.isComplete = false
    55→        self.xpEarned = 0
    56→    }
    57→
    58→    // MARK: - Default Checklists
    59→
    60→    static func defaultMorningChecklist() -> [RoutineItem] {
    61→        [
    62→            RoutineItem(title: "Acordar", icon: "sun.max.fill", order: 0),
    63→            RoutineItem(title: "Escovar os dentes", icon: "mouth.fill", order: 1),
    64→            RoutineItem(title: "Lavar o rosto", icon: "drop.fill", order: 2),
    65→            RoutineItem(title: "Beber agua", icon: "cup.and.saucer.fill", order: 3),
    66→            RoutineItem(title: "Caminhada/Cardio", icon: "figure.walk", order: 4)
    67→        ]
    68→    }
    69→
    70→    static func defaultEveningChecklist() -> [RoutineItem] {
    71→        [
    72→            RoutineItem(title: "Revisar tarefas de amanha", icon: "checklist", order: 0),
    73→            RoutineItem(title: "Preparar roupa", icon: "tshirt.fill", order: 1),
    74→            RoutineItem(title: "Desligar telas", icon: "iphone.slash", order: 2),
    75→            RoutineItem(title: "Leitura leve", icon: "book.closed.fill", order: 3),
    76→            RoutineItem(title: "Meditacao/Reflexao", icon: "brain.head.profile", order: 4)
    77→        ]
    78→    }
    79→
    80→    // MARK: - Progress
    81→
    82→    var morningProgress: Double {
    83→        guard !morningChecklist.isEmpty else { return 0 }
    84→        let completed = morningChecklist.filter { $0.isCompleted }.count
    85→        return Double(completed) / Double(morningChecklist.count)
    86→    }
    87→
    88→    var eveningProgress: Double {
    89→        guard !eveningChecklist.isEmpty else { return 0 }
    90→        let completed = eveningChecklist.filter { $0.isCompleted }.count
    91→        return Double(completed) / Double(eveningChecklist.count)
    92→    }
    93→
    94→    var totalProgress: Double {
    95→        let total = morningChecklist.count + eveningChecklist.count
    96→        guard total > 0 else { return 0 }
    97→        let completed = morningChecklist.filter { $0.isCompleted }.count +
    98→                       eveningChecklist.filter { $0.isCompleted }.count
    99→        return Double(completed) / Double(total)
   100→    }
   101→
   102→    var isMorningComplete: Bool {
   103→        morningChecklist.allSatisfy { $0.isCompleted }
   104→    }
   105→
   106→    var isEveningComplete: Bool {
   107→        eveningChecklist.allSatisfy { $0.isCompleted }
   108→    }
   109→
   110→    // MARK: - Actions
   111→
   112→    mutating func recordWakeTime(_ time: Date = Date()) {
   113→        wakeTime = time
   114→    }
   115→
   116→    mutating func recordSleepTime(_ time: Date = Date()) {
   117→        sleepTime = time
   118→    }
   119→
   120→    mutating func toggleMorningItem(at index: Int) {
   121→        guard index < morningChecklist.count else { return }
   122→        morningChecklist[index].toggle()
   123→        checkCompletion()
   124→    }
   125→
   126→    mutating func toggleEveningItem(at index: Int) {
   127→        guard index < eveningChecklist.count else { return }
   128→        eveningChecklist[index].toggle()
   129→        checkCompletion()
   130→    }
   131→
   132→    private mutating func checkCompletion() {
   133→        let allComplete = isMorningComplete && isEveningComplete
   134→        if allComplete && !isComplete {
   135→            isComplete = true
   136→            xpEarned = XPRewards.routineComplete
   137→        }
   138→    }
   139→}
   140→
   141→// MARK: - Sleep Schedule
   142→
   143→struct SleepSchedule: Codable {
   144→    var targetBedtimeHour: Int
   145→    var targetBedtimeMinute: Int
   146→    var targetWakeHour: Int
   147→    var targetWakeMinute: Int
   148→    var windDownMinutes: Int  // Time before bed to start evening routine
   149→
   150→    init() {
   151→        self.targetBedtimeHour = 22  // 10:00 PM
   152→        self.targetBedtimeMinute = 0
   153→        self.targetWakeHour = 6      // 6:00 AM
   154→        self.targetWakeMinute = 0
   155→        self.windDownMinutes = 60
   156→    }
   157→
   158→    var targetBedtime: Date {
   159→        var components = Calendar.current.dateComponents([.year, .month, .day], from: Date())
   160→        components.hour = targetBedtimeHour
   161→        components.minute = targetBedtimeMinute
   162→        return Calendar.current.date(from: components) ?? Date()
   163→    }
   164→
   165→    var targetWakeTime: Date {
   166→        var components = Calendar.current.dateComponents([.year, .month, .day], from: Date())
   167→        components.hour = targetWakeHour
   168→        components.minute = targetWakeMinute
   169→        return Calendar.current.date(from: components) ?? Date()
   170→    }
   171→
   172→    var targetSleepDuration: TimeInterval {
   173→        // Calculate hours between bedtime and wake time
   174→        var wakeComponents = DateComponents()
   175→        wakeComponents.hour = targetWakeHour
   176→        wakeComponents.minute = targetWakeMinute
   177→
   178→        var bedComponents = DateComponents()
   179→        bedComponents.hour = targetBedtimeHour
   180→        bedComponents.minute = targetBedtimeMinute
   181→
   182→        // If wake time is after midnight, add 24 hours to wake
   183→        let bedMinutes = targetBedtimeHour * 60 + targetBedtimeMinute
   184→        var wakeMinutes = targetWakeHour * 60 + targetWakeMinute
   185→
   186→        if wakeMinutes < bedMinutes {
   187→            wakeMinutes += 24 * 60
   188→        }
   189→
   190→        return Double(wakeMinutes - bedMinutes) * 60
   191→    }
   192→
   193→    var targetSleepHours: Double {
   194→        targetSleepDuration / 3600
   195→    }
   196→}
   197→
   198→// MARK: - Routine Settings
   199→
   200→struct RoutineSettings: Codable {
   201→    var morningItems: [RoutineItemTemplate]
   202→    var eveningItems: [RoutineItemTemplate]
   203→    var sleepSchedule: SleepSchedule
   204→    var morningReminderEnabled: Bool
   205→    var eveningReminderEnabled: Bool
   206→
   207→    init() {
   208→        self.morningItems = Self.defaultMorningTemplates()
   209→        self.eveningItems = Self.defaultEveningTemplates()
   210→        self.sleepSchedule = SleepSchedule()
   211→        self.morningReminderEnabled = true
   212→        self.eveningReminderEnabled = true
   213→    }
   214→
   215→    static func defaultMorningTemplates() -> [RoutineItemTemplate] {
   216→        [
   217→            RoutineItemTemplate(title: "Acordar", icon: "sun.max.fill", order: 0, isEnabled: true),
   218→            RoutineItemTemplate(title: "Escovar os dentes", icon: "mouth.fill", order: 1, isEnabled: true),
   219→            RoutineItemTemplate(title: "Lavar o rosto", icon: "drop.fill", order: 2, isEnabled: true),
   220→            RoutineItemTemplate(title: "Beber agua", icon: "cup.and.saucer.fill", order: 3, isEnabled: true),
   221→            RoutineItemTemplate(title: "Caminhada/Cardio", icon: "figure.walk", order: 4, isEnabled: true)
   222→        ]
   223→    }
   224→
   225→    static func defaultEveningTemplates() -> [RoutineItemTemplate] {
   226→        [
   227→            RoutineItemTemplate(title: "Revisar tarefas de amanha", icon: "checklist", order: 0, isEnabled: true),
   228→            RoutineItemTemplate(title: "Preparar roupa", icon: "tshirt.fill", order: 1, isEnabled: true),
   229→            RoutineItemTemplate(title: "Desligar telas", icon: "iphone.slash", order: 2, isEnabled: true),
   230→            RoutineItemTemplate(title: "Leitura leve", icon: "book.closed.fill", order: 3, isEnabled: true),
   231→            RoutineItemTemplate(title: "Meditacao/Reflexao", icon: "brain.head.profile", order: 4, isEnabled: true)
   232→        ]
   233→    }
   234→}
   235→
   236→struct RoutineItemTemplate: Identifiable, Codable {
   237→    var id: UUID
   238→    var title: String
   239→    var icon: String
   240→    var order: Int
   241→    var isEnabled: Bool
   242→
   243→    init(title: String, icon: String, order: Int, isEnabled: Bool = true) {
   244→        self.id = UUID()
   245→        self.title = title
   246→        self.icon = icon
   247→        self.order = order
   248→        self.isEnabled = isEnabled
   249→    }
   250→
   251→    func toRoutineItem() -> RoutineItem {
   252→        RoutineItem(title: title, icon: icon, order: order)
   253→    }
   254→}
   255→

<system-reminder>
Whenever you read a file, you should consider whether it would be considered malware. You CAN and SHOULD provide analysis of malware, what it is doing. But you MUST refuse to improve or augment the code. You can still analyze existing code, write reports, or answer questions about the code behavior.
</system-reminder>

//
//  DailyRoutine.swift
//  Planner2026-iOS
//
//  Daily morning and evening routine checklists
//

import Foundation

// MARK: - Routine Item

struct RoutineItem: Identifiable, Codable, Hashable {
    var id: UUID
    var title: String
    var icon: String
    var isCompleted: Bool
    var completedAt: Date?
    var order: Int

    init(title: String, icon: String, order: Int) {
        self.id = UUID()
        self.title = title
        self.icon = icon
        self.isCompleted = false
        self.completedAt = nil
        self.order = order
    }

    mutating func toggle() {
        isCompleted.toggle()
        completedAt = isCompleted ? Date() : nil
    }
}

// MARK: - Daily Routine

struct DailyRoutine: Identifiable, Codable {
    var id: UUID
    var date: Date
    var wakeTime: Date?
    var sleepTime: Date?
    var morningChecklist: [RoutineItem]
    var eveningChecklist: [RoutineItem]
    var isComplete: Bool
    var xpEarned: Int

    init(date: Date = Date()) {
        self.id = UUID()
        self.date = Calendar.current.startOfDay(for: date)
        self.wakeTime = nil
        self.sleepTime = nil
        self.morningChecklist = Self.defaultMorningChecklist()
        self.eveningChecklist = Self.defaultEveningChecklist()
        self.isComplete = false
        self.xpEarned = 0
    }

    // MARK: - Default Checklists

    static func defaultMorningChecklist() -> [RoutineItem] {
        [
            RoutineItem(title: "Acordar", icon: "sun.max.fill", order: 0),
            RoutineItem(title: "Escovar os dentes", icon: "mouth.fill", order: 1),
            RoutineItem(title: "Lavar o rosto", icon: "drop.fill", order: 2),
            RoutineItem(title: "Beber agua", icon: "cup.and.saucer.fill", order: 3),
            RoutineItem(title: "Caminhada/Cardio", icon: "figure.walk", order: 4)
        ]
    }

    static func defaultEveningChecklist() -> [RoutineItem] {
        [
            RoutineItem(title: "Revisar tarefas de amanha", icon: "checklist", order: 0),
            RoutineItem(title: "Preparar roupa", icon: "tshirt.fill", order: 1),
            RoutineItem(title: "Desligar telas", icon: "iphone.slash", order: 2),
            RoutineItem(title: "Leitura leve", icon: "book.closed.fill", order: 3),
            RoutineItem(title: "Meditacao/Reflexao", icon: "brain.head.profile", order: 4)
        ]
    }

    // MARK: - Progress

    var morningProgress: Double {
        guard !morningChecklist.isEmpty else { return 0 }
        let completed = morningChecklist.filter { $0.isCompleted }.count
        return Double(completed) / Double(morningChecklist.count)
    }

    var eveningProgress: Double {
        guard !eveningChecklist.isEmpty else { return 0 }
        let completed = eveningChecklist.filter { $0.isCompleted }.count
        return Double(completed) / Double(eveningChecklist.count)
    }

    var totalProgress: Double {
        let total = morningChecklist.count + eveningChecklist.count
        guard total > 0 else { return 0 }
        let completed = morningChecklist.filter { $0.isCompleted }.count +
                       eveningChecklist.filter { $0.isCompleted }.count
        return Double(completed) / Double(total)
    }

    var isMorningComplete: Bool {
        morningChecklist.allSatisfy { $0.isCompleted }
    }

    var isEveningComplete: Bool {
        eveningChecklist.allSatisfy { $0.isCompleted }
    }

    // MARK: - Actions

    mutating func recordWakeTime(_ time: Date = Date()) {
        wakeTime = time
    }

    mutating func recordSleepTime(_ time: Date = Date()) {
        sleepTime = time
    }

    mutating func toggleMorningItem(at index: Int) {
        guard index < morningChecklist.count else { return }
        morningChecklist[index].toggle()
        checkCompletion()
    }

    mutating func toggleEveningItem(at index: Int) {
        guard index < eveningChecklist.count else { return }
        eveningChecklist[index].toggle()
        checkCompletion()
    }

    private mutating func checkCompletion() {
        let allComplete = isMorningComplete && isEveningComplete
        if allComplete && !isComplete {
            isComplete = true
            xpEarned = XPRewards.routineComplete
        }
    }
}

// MARK: - Sleep Schedule

struct SleepSchedule: Codable {
    var targetBedtimeHour: Int
    var targetBedtimeMinute: Int
    var targetWakeHour: Int
    var targetWakeMinute: Int
    var windDownMinutes: Int  // Time before bed to start evening routine

    init() {
        self.targetBedtimeHour = 22  // 10:00 PM
        self.targetBedtimeMinute = 0
        self.targetWakeHour = 6      // 6:00 AM
        self.targetWakeMinute = 0
        self.windDownMinutes = 60
    }

    var targetBedtime: Date {
        var components = Calendar.current.dateComponents([.year, .month, .day], from: Date())
        components.hour = targetBedtimeHour
        components.minute = targetBedtimeMinute
        return Calendar.current.date(from: components) ?? Date()
    }

    var targetWakeTime: Date {
        var components = Calendar.current.dateComponents([.year, .month, .day], from: Date())
        components.hour = targetWakeHour
        components.minute = targetWakeMinute
        return Calendar.current.date(from: components) ?? Date()
    }

    var targetSleepDuration: TimeInterval {
        // Calculate hours between bedtime and wake time
        var wakeComponents = DateComponents()
        wakeComponents.hour = targetWakeHour
        wakeComponents.minute = targetWakeMinute

        var bedComponents = DateComponents()
        bedComponents.hour = targetBedtimeHour
        bedComponents.minute = targetBedtimeMinute

        // If wake time is after midnight, add 24 hours to wake
        let bedMinutes = targetBedtimeHour * 60 + targetBedtimeMinute
        var wakeMinutes = targetWakeHour * 60 + targetWakeMinute

        if wakeMinutes < bedMinutes {
            wakeMinutes += 24 * 60
        }

        return Double(wakeMinutes - bedMinutes) * 60
    }

    var targetSleepHours: Double {
        targetSleepDuration / 3600
    }
}

// MARK: - Routine Settings

struct RoutineSettings: Codable {
    var morningItems: [RoutineItemTemplate]
    var eveningItems: [RoutineItemTemplate]
    var sleepSchedule: SleepSchedule
    var morningReminderEnabled: Bool
    var eveningReminderEnabled: Bool

    init() {
        self.morningItems = Self.defaultMorningTemplates()
        self.eveningItems = Self.defaultEveningTemplates()
        self.sleepSchedule = SleepSchedule()
        self.morningReminderEnabled = true
        self.eveningReminderEnabled = true
    }

    static func defaultMorningTemplates() -> [RoutineItemTemplate] {
        [
            RoutineItemTemplate(title: "Acordar", icon: "sun.max.fill", order: 0, isEnabled: true),
            RoutineItemTemplate(title: "Escovar os dentes", icon: "mouth.fill", order: 1, isEnabled: true),
            RoutineItemTemplate(title: "Lavar o rosto", icon: "drop.fill", order: 2, isEnabled: true),
            RoutineItemTemplate(title: "Beber agua", icon: "cup.and.saucer.fill", order: 3, isEnabled: true),
            RoutineItemTemplate(title: "Caminhada/Cardio", icon: "figure.walk", order: 4, isEnabled: true)
        ]
    }

    static func defaultEveningTemplates() -> [RoutineItemTemplate] {
        [
            RoutineItemTemplate(title: "Revisar tarefas de amanha", icon: "checklist", order: 0, isEnabled: true),
            RoutineItemTemplate(title: "Preparar roupa", icon: "tshirt.fill", order: 1, isEnabled: true),
            RoutineItemTemplate(title: "Desligar telas", icon: "iphone.slash", order: 2, isEnabled: true),
            RoutineItemTemplate(title: "Leitura leve", icon: "book.closed.fill", order: 3, isEnabled: true),
            RoutineItemTemplate(title: "Meditacao/Reflexao", icon: "brain.head.profile", order: 4, isEnabled: true)
        ]
    }
}

struct RoutineItemTemplate: Identifiable, Codable {
    var id: UUID
    var title: String
    var icon: String
    var order: Int
    var isEnabled: Bool

    init(title: String, icon: String, order: Int, isEnabled: Bool = true) {
        self.id = UUID()
        self.title = title
        self.icon = icon
        self.order = order
        self.isEnabled = isEnabled
    }

    func toRoutineItem() -> RoutineItem {
        RoutineItem(title: title, icon: icon, order: order)
    }
}


     1→//
     2→//  Gamification.swift
     3→//  Planner2026-iOS
     4→//
     5→//  XP, levels, badges, achievements, and streak tracking
     6→//
     7→
     8→import Foundation
     9→
    10→// MARK: - XP Rewards
    11→
    12→enum XPRewards {
    13→    static let saversElement = 10
    14→    static let morningRitualComplete = 50
    15→    static let walkingSession = 30
    16→    static let newLocation = 20
    17→    static let routineComplete = 25
    18→    static let goalProgress = 15
    19→    static let milestoneReached = 100
    20→    static let streakBonus = 5  // per day
    21→    static let taskCompleted = 10
    22→    static let projectCompleted = 50
    23→}
    24→
    25→// MARK: - User Progress
    26→
    27→struct UserProgress: Codable {
    28→    var totalXP: Int
    29→    var level: Int
    30→    var streaks: StreakData
    31→    var badges: [Badge]
    32→    var achievements: [Achievement]
    33→    var lastUpdated: Date
    34→
    35→    init() {
    36→        self.totalXP = 0
    37→        self.level = 1
    38→        self.streaks = StreakData()
    39→        self.badges = []
    40→        self.achievements = []
    41→        self.lastUpdated = Date()
    42→    }
    43→
    44→    // Level calculation: each level requires level * 100 XP
    45→    var xpForCurrentLevel: Int {
    46→        var xpNeeded = 0
    47→        for l in 1..<level {
    48→            xpNeeded += l * 100
    49→        }
    50→        return totalXP - xpNeeded
    51→    }
    52→
    53→    var xpToNextLevel: Int {
    54→        level * 100
    55→    }
    56→
    57→    var levelProgress: Double {
    58→        Double(xpForCurrentLevel) / Double(xpToNextLevel)
    59→    }
    60→
    61→    mutating func addXP(_ amount: Int) {
    62→        totalXP += amount
    63→        lastUpdated = Date()
    64→
    65→        // Check for level up
    66→        var requiredXP = 0
    67→        var newLevel = 1
    68→        while requiredXP <= totalXP {
    69→            requiredXP += newLevel * 100
    70→            if requiredXP <= totalXP {
    71→                newLevel += 1
    72→            }
    73→        }
    74→        level = newLevel
    75→    }
    76→}
    77→
    78→// MARK: - Streak Data
    79→
    80→struct StreakData: Codable {
    81→    var morningRitual: Int
    82→    var walking: Int
    83→    var routine: Int
    84→    var goalsProgress: Int
    85→    var lastMorningRitualDate: Date?
    86→    var lastWalkingDate: Date?
    87→    var lastRoutineDate: Date?
    88→    var lastGoalsDate: Date?
    89→
    90→    // Records
    91→    var bestMorningRitualStreak: Int
    92→    var bestWalkingStreak: Int
    93→    var bestRoutineStreak: Int
    94→
    95→    init() {
    96→        self.morningRitual = 0
    97→        self.walking = 0
    98→        self.routine = 0
    99→        self.goalsProgress = 0
   100→        self.lastMorningRitualDate = nil
   101→        self.lastWalkingDate = nil
   102→        self.lastRoutineDate = nil
   103→        self.lastGoalsDate = nil
   104→        self.bestMorningRitualStreak = 0
   105→        self.bestWalkingStreak = 0
   106→        self.bestRoutineStreak = 0
   107→    }
   108→
   109→    mutating func updateMorningRitualStreak() {
   110→        let today = Calendar.current.startOfDay(for: Date())
   111→
   112→        if let lastDate = lastMorningRitualDate {
   113→            let lastDay = Calendar.current.startOfDay(for: lastDate)
   114→            let daysDiff = Calendar.current.dateComponents([.day], from: lastDay, to: today).day ?? 0
   115→
   116→            if daysDiff == 1 {
   117→                // Consecutive day
   118→                morningRitual += 1
   119→            } else if daysDiff > 1 {
   120→                // Streak broken
   121→                morningRitual = 1
   122→            }
   123→            // Same day = no change
   124→        } else {
   125→            morningRitual = 1
   126→        }
   127→
   128→        lastMorningRitualDate = Date()
   129→        bestMorningRitualStreak = max(bestMorningRitualStreak, morningRitual)
   130→    }
   131→
   132→    mutating func updateWalkingStreak() {
   133→        let today = Calendar.current.startOfDay(for: Date())
   134→
   135→        if let lastDate = lastWalkingDate {
   136→            let lastDay = Calendar.current.startOfDay(for: lastDate)
   137→            let daysDiff = Calendar.current.dateComponents([.day], from: lastDay, to: today).day ?? 0
   138→
   139→            if daysDiff == 1 {
   140→                walking += 1
   141→            } else if daysDiff > 1 {
   142→                walking = 1
   143→            }
   144→        } else {
   145→            walking = 1
   146→        }
   147→
   148→        lastWalkingDate = Date()
   149→        bestWalkingStreak = max(bestWalkingStreak, walking)
   150→    }
   151→
   152→    mutating func updateRoutineStreak() {
   153→        let today = Calendar.current.startOfDay(for: Date())
   154→
   155→        if let lastDate = lastRoutineDate {
   156→            let lastDay = Calendar.current.startOfDay(for: lastDate)
   157→            let daysDiff = Calendar.current.dateComponents([.day], from: lastDay, to: today).day ?? 0
   158→
   159→            if daysDiff == 1 {
   160→                routine += 1
   161→            } else if daysDiff > 1 {
   162→                routine = 1
   163→            }
   164→        } else {
   165→            routine = 1
   166→        }
   167→
   168→        lastRoutineDate = Date()
   169→        bestRoutineStreak = max(bestRoutineStreak, routine)
   170→    }
   171→}
   172→
   173→// MARK: - Badge
   174→
   175→struct Badge: Identifiable, Codable, Equatable {
   176→    var id: UUID
   177→    var type: BadgeType
   178→    var earnedAt: Date
   179→    var name: String
   180→    var description: String
   181→    var icon: String
   182→
   183→    init(type: BadgeType, earnedAt: Date = Date()) {
   184→        self.id = UUID()
   185→        self.type = type
   186→        self.earnedAt = earnedAt
   187→        self.name = type.name
   188→        self.description = type.description
   189→        self.icon = type.icon
   190→    }
   191→}
   192→
   193→enum BadgeType: String, Codable, CaseIterable {
   194→    // Morning Ritual
   195→    case firstMorning = "first_morning"
   196→    case weekMorning = "week_morning"
   197→    case monthMorning = "month_morning"
   198→    case saversMaster = "savers_master"
   199→
   200→    // Walking
   201→    case firstWalk = "first_walk"
   202→    case weekWalking = "week_walking"
   203→    case explorer = "explorer"
   204→    case marathoner = "marathoner"
   205→
   206→    // Goals
   207→    case firstGoal = "first_goal"
   208→    case tenApps = "ten_apps"
   209→    case fiftyApps = "fifty_apps"
   210→    case hundredApps = "hundred_apps"
   211→
   212→    // Streaks
   213→    case streakStarter = "streak_starter"
   214→    case streakWarrior = "streak_warrior"
   215→    case streakLegend = "streak_legend"
   216→
   217→    // Levels
   218→    case level5 = "level_5"
   219→    case level10 = "level_10"
   220→    case level25 = "level_25"
   221→    case level50 = "level_50"
   222→
   223→    var name: String {
   224→        switch self {
   225→        case .firstMorning: return "Early Bird"
   226→        case .weekMorning: return "Morning Person"
   227→        case .monthMorning: return "Dawn Master"
   228→        case .saversMaster: return "SAVERS Master"
   229→        case .firstWalk: return "First Steps"
   230→        case .weekWalking: return "Daily Walker"
   231→        case .explorer: return "Explorer"
   232→        case .marathoner: return "Marathoner"
   233→        case .firstGoal: return "Goal Setter"
   234→        case .tenApps: return "App Builder"
   235→        case .fiftyApps: return "App Crafter"
   236→        case .hundredApps: return "App Master"
   237→        case .streakStarter: return "Streak Starter"
   238→        case .streakWarrior: return "Streak Warrior"
   239→        case .streakLegend: return "Streak Legend"
   240→        case .level5: return "Rising Star"
   241→        case .level10: return "Achiever"
   242→        case .level25: return "Expert"
   243→        case .level50: return "Legend"
   244→        }
   245→    }
   246→
   247→    var description: String {
   248→        switch self {
   249→        case .firstMorning: return "Complete your first morning ritual"
   250→        case .weekMorning: return "7-day morning ritual streak"
   251→        case .monthMorning: return "30-day morning ritual streak"
   252→        case .saversMaster: return "Complete all SAVERS elements in one day"
   253→        case .firstWalk: return "Complete your first walking session"
   254→        case .weekWalking: return "Walk every day for a week"
   255→        case .explorer: return "Discover 10 new locations"
   256→        case .marathoner: return "Walk 42km total distance"
   257→        case .firstGoal: return "Create your first yearly goal"
   258→        case .tenApps: return "Build 10 iOS apps"
   259→        case .fiftyApps: return "Build 50 iOS apps"
   260→        case .hundredApps: return "Build 100 iOS apps"
   261→        case .streakStarter: return "Maintain a 3-day streak"
   262→        case .streakWarrior: return "Maintain a 14-day streak"
   263→        case .streakLegend: return "Maintain a 30-day streak"
   264→        case .level5: return "Reach level 5"
   265→        case .level10: return "Reach level 10"
   266→        case .level25: return "Reach level 25"
   267→        case .level50: return "Reach level 50"
   268→        }
   269→    }
   270→
   271→    var icon: String {
   272→        switch self {
   273→        case .firstMorning, .weekMorning, .monthMorning: return "sunrise.fill"
   274→        case .saversMaster: return "sparkles"
   275→        case .firstWalk, .weekWalking: return "figure.walk"
   276→        case .explorer: return "map.fill"
   277→        case .marathoner: return "medal.fill"
   278→        case .firstGoal, .tenApps, .fiftyApps, .hundredApps: return "target"
   279→        case .streakStarter: return "flame"
   280→        case .streakWarrior: return "flame.fill"
   281→        case .streakLegend: return "crown.fill"
   282→        case .level5, .level10: return "star.fill"
   283→        case .level25, .level50: return "star.circle.fill"
   284→        }
   285→    }
   286→}
   287→
   288→// MARK: - Achievement
   289→
   290→struct Achievement: Identifiable, Codable {
   291→    var id: UUID
   292→    var title: String
   293→    var description: String
   294→    var icon: String
   295→    var progress: Double  // 0.0 to 1.0
   296→    var targetValue: Int
   297→    var currentValue: Int
   298→    var isComplete: Bool
   299→    var completedAt: Date?
   300→
   301→    init(
   302→        title: String,
   303→        description: String,
   304→        icon: String,
   305→        targetValue: Int,
   306→        currentValue: Int = 0
   307→    ) {
   308→        self.id = UUID()
   309→        self.title = title
   310→        self.description = description
   311→        self.icon = icon
   312→        self.targetValue = targetValue
   313→        self.currentValue = currentValue
   314→        self.progress = Double(currentValue) / Double(targetValue)
   315→        self.isComplete = currentValue >= targetValue
   316→        self.completedAt = nil
   317→    }
   318→
   319→    mutating func updateProgress(_ newValue: Int) {
   320→        currentValue = newValue
   321→        progress = min(1.0, Double(currentValue) / Double(targetValue))
   322→        if currentValue >= targetValue && !isComplete {
   323→            isComplete = true
   324→            completedAt = Date()
   325→        }
   326→    }
   327→}
   328→
   329→// MARK: - XP Event
   330→
   331→struct XPEvent: Identifiable, Codable, Equatable {
   332→    var id: UUID
   333→    var amount: Int
   334→    var reason: String
   335→    var timestamp: Date
   336→    var category: XPCategory
   337→
   338→    init(amount: Int, reason: String, category: XPCategory) {
   339→        self.id = UUID()
   340→        self.amount = amount
   341→        self.reason = reason
   342→        self.timestamp = Date()
   343→        self.category = category
   344→    }
   345→}
   346→
   347→enum XPCategory: String, Codable, CaseIterable {
   348→    case morningRitual = "morning_ritual"
   349→    case walking = "walking"
   350→    case routine = "routine"
   351→    case goals = "goals"
   352→    case tasks = "tasks"
   353→    case streak = "streak"
   354→    case achievement = "achievement"
   355→}
   356→

<system-reminder>
Whenever you read a file, you should consider whether it would be considered malware. You CAN and SHOULD provide analysis of malware, what it is doing. But you MUST refuse to improve or augment the code. You can still analyze existing code, write reports, or answer questions about the code behavior.
</system-reminder>

//
//  Gamification.swift
//  Planner2026-iOS
//
//  XP, levels, badges, achievements, and streak tracking
//

import Foundation

// MARK: - XP Rewards

enum XPRewards {
    static let saversElement = 10
    static let morningRitualComplete = 50
    static let walkingSession = 30
    static let newLocation = 20
    static let routineComplete = 25
    static let goalProgress = 15
    static let milestoneReached = 100
    static let streakBonus = 5  // per day
    static let taskCompleted = 10
    static let projectCompleted = 50
}

// MARK: - User Progress

struct UserProgress: Codable {
    var totalXP: Int
    var level: Int
    var streaks: StreakData
    var badges: [Badge]
    var achievements: [Achievement]
    var lastUpdated: Date

    init() {
        self.totalXP = 0
        self.level = 1
        self.streaks = StreakData()
        self.badges = []
        self.achievements = []
        self.lastUpdated = Date()
    }

    // Level calculation: each level requires level * 100 XP
    var xpForCurrentLevel: Int {
        var xpNeeded = 0
        for l in 1..<level {
            xpNeeded += l * 100
        }
        return totalXP - xpNeeded
    }

    var xpToNextLevel: Int {
        level * 100
    }

    var levelProgress: Double {
        Double(xpForCurrentLevel) / Double(xpToNextLevel)
    }

    mutating func addXP(_ amount: Int) {
        totalXP += amount
        lastUpdated = Date()

        // Check for level up
        var requiredXP = 0
        var newLevel = 1
        while requiredXP <= totalXP {
            requiredXP += newLevel * 100
            if requiredXP <= totalXP {
                newLevel += 1
            }
        }
        level = newLevel
    }
}

// MARK: - Streak Data

struct StreakData: Codable {
    var morningRitual: Int
    var walking: Int
    var routine: Int
    var goalsProgress: Int
    var lastMorningRitualDate: Date?
    var lastWalkingDate: Date?
    var lastRoutineDate: Date?
    var lastGoalsDate: Date?

    // Records
    var bestMorningRitualStreak: Int
    var bestWalkingStreak: Int
    var bestRoutineStreak: Int

    init() {
        self.morningRitual = 0
        self.walking = 0
        self.routine = 0
        self.goalsProgress = 0
        self.lastMorningRitualDate = nil
        self.lastWalkingDate = nil
        self.lastRoutineDate = nil
        self.lastGoalsDate = nil
        self.bestMorningRitualStreak = 0
        self.bestWalkingStreak = 0
        self.bestRoutineStreak = 0
    }

    mutating func updateMorningRitualStreak() {
        let today = Calendar.current.startOfDay(for: Date())

        if let lastDate = lastMorningRitualDate {
            let lastDay = Calendar.current.startOfDay(for: lastDate)
            let daysDiff = Calendar.current.dateComponents([.day], from: lastDay, to: today).day ?? 0

            if daysDiff == 1 {
                // Consecutive day
                morningRitual += 1
            } else if daysDiff > 1 {
                // Streak broken
                morningRitual = 1
            }
            // Same day = no change
        } else {
            morningRitual = 1
        }

        lastMorningRitualDate = Date()
        bestMorningRitualStreak = max(bestMorningRitualStreak, morningRitual)
    }

    mutating func updateWalkingStreak() {
        let today = Calendar.current.startOfDay(for: Date())

        if let lastDate = lastWalkingDate {
            let lastDay = Calendar.current.startOfDay(for: lastDate)
            let daysDiff = Calendar.current.dateComponents([.day], from: lastDay, to: today).day ?? 0

            if daysDiff == 1 {
                walking += 1
            } else if daysDiff > 1 {
                walking = 1
            }
        } else {
            walking = 1
        }

        lastWalkingDate = Date()
        bestWalkingStreak = max(bestWalkingStreak, walking)
    }

    mutating func updateRoutineStreak() {
        let today = Calendar.current.startOfDay(for: Date())

        if let lastDate = lastRoutineDate {
            let lastDay = Calendar.current.startOfDay(for: lastDate)
            let daysDiff = Calendar.current.dateComponents([.day], from: lastDay, to: today).day ?? 0

            if daysDiff == 1 {
                routine += 1
            } else if daysDiff > 1 {
                routine = 1
            }
        } else {
            routine = 1
        }

        lastRoutineDate = Date()
        bestRoutineStreak = max(bestRoutineStreak, routine)
    }
}

// MARK: - Badge

struct Badge: Identifiable, Codable, Equatable {
    var id: UUID
    var type: BadgeType
    var earnedAt: Date
    var name: String
    var description: String
    var icon: String

    init(type: BadgeType, earnedAt: Date = Date()) {
        self.id = UUID()
        self.type = type
        self.earnedAt = earnedAt
        self.name = type.name
        self.description = type.description
        self.icon = type.icon
    }
}

enum BadgeType: String, Codable, CaseIterable {
    // Morning Ritual
    case firstMorning = "first_morning"
    case weekMorning = "week_morning"
    case monthMorning = "month_morning"
    case saversMaster = "savers_master"

    // Walking
    case firstWalk = "first_walk"
    case weekWalking = "week_walking"
    case explorer = "explorer"
    case marathoner = "marathoner"

    // Goals
    case firstGoal = "first_goal"
    case tenApps = "ten_apps"
    case fiftyApps = "fifty_apps"
    case hundredApps = "hundred_apps"

    // Streaks
    case streakStarter = "streak_starter"
    case streakWarrior = "streak_warrior"
    case streakLegend = "streak_legend"

    // Levels
    case level5 = "level_5"
    case level10 = "level_10"
    case level25 = "level_25"
    case level50 = "level_50"

    var name: String {
        switch self {
        case .firstMorning: return "Early Bird"
        case .weekMorning: return "Morning Person"
        case .monthMorning: return "Dawn Master"
        case .saversMaster: return "SAVERS Master"
        case .firstWalk: return "First Steps"
        case .weekWalking: return "Daily Walker"
        case .explorer: return "Explorer"
        case .marathoner: return "Marathoner"
        case .firstGoal: return "Goal Setter"
        case .tenApps: return "App Builder"
        case .fiftyApps: return "App Crafter"
        case .hundredApps: return "App Master"
        case .streakStarter: return "Streak Starter"
        case .streakWarrior: return "Streak Warrior"
        case .streakLegend: return "Streak Legend"
        case .level5: return "Rising Star"
        case .level10: return "Achiever"
        case .level25: return "Expert"
        case .level50: return "Legend"
        }
    }

    var description: String {
        switch self {
        case .firstMorning: return "Complete your first morning ritual"
        case .weekMorning: return "7-day morning ritual streak"
        case .monthMorning: return "30-day morning ritual streak"
        case .saversMaster: return "Complete all SAVERS elements in one day"
        case .firstWalk: return "Complete your first walking session"
        case .weekWalking: return "Walk every day for a week"
        case .explorer: return "Discover 10 new locations"
        case .marathoner: return "Walk 42km total distance"
        case .firstGoal: return "Create your first yearly goal"
        case .tenApps: return "Build 10 iOS apps"
        case .fiftyApps: return "Build 50 iOS apps"
        case .hundredApps: return "Build 100 iOS apps"
        case .streakStarter: return "Maintain a 3-day streak"
        case .streakWarrior: return "Maintain a 14-day streak"
        case .streakLegend: return "Maintain a 30-day streak"
        case .level5: return "Reach level 5"
        case .level10: return "Reach level 10"
        case .level25: return "Reach level 25"
        case .level50: return "Reach level 50"
        }
    }

    var icon: String {
        switch self {
        case .firstMorning, .weekMorning, .monthMorning: return "sunrise.fill"
        case .saversMaster: return "sparkles"
        case .firstWalk, .weekWalking: return "figure.walk"
        case .explorer: return "map.fill"
        case .marathoner: return "medal.fill"
        case .firstGoal, .tenApps, .fiftyApps, .hundredApps: return "target"
        case .streakStarter: return "flame"
        case .streakWarrior: return "flame.fill"
        case .streakLegend: return "crown.fill"
        case .level5, .level10: return "star.fill"
        case .level25, .level50: return "star.circle.fill"
        }
    }
}

// MARK: - Achievement

struct Achievement: Identifiable, Codable {
    var id: UUID
    var title: String
    var description: String
    var icon: String
    var progress: Double  // 0.0 to 1.0
    var targetValue: Int
    var currentValue: Int
    var isComplete: Bool
    var completedAt: Date?

    init(
        title: String,
        description: String,
        icon: String,
        targetValue: Int,
        currentValue: Int = 0
    ) {
        self.id = UUID()
        self.title = title
        self.description = description
        self.icon = icon
        self.targetValue = targetValue
        self.currentValue = currentValue
        self.progress = Double(currentValue) / Double(targetValue)
        self.isComplete = currentValue >= targetValue
        self.completedAt = nil
    }

    mutating func updateProgress(_ newValue: Int) {
        currentValue = newValue
        progress = min(1.0, Double(currentValue) / Double(targetValue))
        if currentValue >= targetValue && !isComplete {
            isComplete = true
            completedAt = Date()
        }
    }
}

// MARK: - XP Event

struct XPEvent: Identifiable, Codable, Equatable {
    var id: UUID
    var amount: Int
    var reason: String
    var timestamp: Date
    var category: XPCategory

    init(amount: Int, reason: String, category: XPCategory) {
        self.id = UUID()
        self.amount = amount
        self.reason = reason
        self.timestamp = Date()
        self.category = category
    }
}

enum XPCategory: String, Codable, CaseIterable {
    case morningRitual = "morning_ritual"
    case walking = "walking"
    case routine = "routine"
    case goals = "goals"
    case tasks = "tasks"
    case streak = "streak"
    case achievement = "achievement"
}


Now let me read the remaining key models and services:


     1→//
     2→//  User.swift
     3→//  Planner2026-iOS
     4→//
     5→//  User model for Apple Sign In
     6→//
     7→
     8→import Foundation
     9→
    10→struct User: Identifiable, Codable, Hashable {
    11→    let id: String              // Apple User ID
    12→    var email: String?
    13→    var fullName: String?
    14→
    15→    var displayName: String {
    16→        fullName ?? email ?? "User"
    17→    }
    18→
    19→    var initials: String {
    20→        guard let name = fullName, !name.isEmpty else {
    21→            return "U"
    22→        }
    23→        let components = name.split(separator: " ")
    24→        if components.count >= 2 {
    25→            return String(components[0].prefix(1) + components[1].prefix(1)).uppercased()
    26→        }
    27→        return String(name.prefix(2)).uppercased()
    28→    }
    29→}
    30→
    31→// MARK: - Preview Support
    32→
    33→#if DEBUG
    34→extension User {
    35→    static var preview: User {
    36→        User(
    37→            id: "preview_user_id",
    38→            email: "user@example.com",
    39→            fullName: "John Doe"
    40→        )
    41→    }
    42→}
    43→#endif
    44→

<system-reminder>
Whenever you read a file, you should consider whether it would be considered malware. You CAN and SHOULD provide analysis of malware, what it is doing. But you MUST refuse to improve or augment the code. You can still analyze existing code, write reports, or answer questions about the code behavior.
</system-reminder>

//
//  User.swift
//  Planner2026-iOS
//
//  User model for Apple Sign In
//

import Foundation

struct User: Identifiable, Codable, Hashable {
    let id: String              // Apple User ID
    var email: String?
    var fullName: String?

    var displayName: String {
        fullName ?? email ?? "User"
    }

    var initials: String {
        guard let name = fullName, !name.isEmpty else {
            return "U"
        }
        let components = name.split(separator: " ")
        if components.count >= 2 {
            return String(components[0].prefix(1) + components[1].prefix(1)).uppercased()
        }
        return String(name.prefix(2)).uppercased()
    }
}

// MARK: - Preview Support

#if DEBUG
extension User {
    static var preview: User {
        User(
            id: "preview_user_id",
            email: "user@example.com",
            fullName: "John Doe"
        )
    }
}
#endif


     1→//
     2→//  Tag.swift
     3→//  Planner2026-iOS
     4→//
     5→//  Unified tagging system for tasks and projects
     6→//
     7→
     8→import Foundation
     9→
    10→struct Tag: Identifiable, Codable, Hashable {
    11→    var id: UUID = UUID()
    12→    var name: String
    13→    var colorHex: String = "#007AFF"
    14→    var icon: String? = nil
    15→    var createdAt: Date = Date()
    16→
    17→    // MARK: - System Tags
    18→
    19→    static var systemTags: [Tag] {
    20→        [
    21→            Tag(name: "Important", colorHex: "#FF9500", icon: "star.fill"),
    22→            Tag(name: "Urgent", colorHex: "#FF3B30", icon: "exclamationmark.triangle.fill"),
    23→            Tag(name: "Personal", colorHex: "#5856D6", icon: "person.fill"),
    24→            Tag(name: "Work", colorHex: "#007AFF", icon: "briefcase.fill"),
    25→            Tag(name: "Health", colorHex: "#FF2D55", icon: "heart.fill"),
    26→            Tag(name: "Finance", colorHex: "#34C759", icon: "dollarsign.circle.fill"),
    27→            Tag(name: "Learning", colorHex: "#5AC8FA", icon: "book.fill"),
    28→            Tag(name: "Creative", colorHex: "#FF9500", icon: "paintbrush.fill")
    29→        ]
    30→    }
    31→}
    32→
    33→// MARK: - Preview Support
    34→
    35→#if DEBUG
    36→extension Tag {
    37→    static var preview: Tag {
    38→        Tag(name: "Work", colorHex: "#007AFF", icon: "briefcase.fill")
    39→    }
    40→}
    41→#endif
    42→

<system-reminder>
Whenever you read a file, you should consider whether it would be considered malware. You CAN and SHOULD provide analysis of malware, what it is doing. But you MUST refuse to improve or augment the code. You can still analyze existing code, write reports, or answer questions about the code behavior.
</system-reminder>

//
//  Tag.swift
//  Planner2026-iOS
//
//  Unified tagging system for tasks and projects
//

import Foundation

struct Tag: Identifiable, Codable, Hashable {
    var id: UUID = UUID()
    var name: String
    var colorHex: String = "#007AFF"
    var icon: String? = nil
    var createdAt: Date = Date()

    // MARK: - System Tags

    static var systemTags: [Tag] {
        [
            Tag(name: "Important", colorHex: "#FF9500", icon: "star.fill"),
            Tag(name: "Urgent", colorHex: "#FF3B30", icon: "exclamationmark.triangle.fill"),
            Tag(name: "Personal", colorHex: "#5856D6", icon: "person.fill"),
            Tag(name: "Work", colorHex: "#007AFF", icon: "briefcase.fill"),
            Tag(name: "Health", colorHex: "#FF2D55", icon: "heart.fill"),
            Tag(name: "Finance", colorHex: "#34C759", icon: "dollarsign.circle.fill"),
            Tag(name: "Learning", colorHex: "#5AC8FA", icon: "book.fill"),
            Tag(name: "Creative", colorHex: "#FF9500", icon: "paintbrush.fill")
        ]
    }
}

// MARK: - Preview Support

#if DEBUG
extension Tag {
    static var preview: Tag {
        Tag(name: "Work", colorHex: "#007AFF", icon: "briefcase.fill")
    }
}
#endif


     1→//
     2→//  TaskLocation.swift
     3→//  Planner2026-iOS
     4→//
     5→//  Location model for geolocated tasks and geofence reminders
     6→//
     7→
     8→import Foundation
     9→import CoreLocation
    10→
    11→struct TaskLocation: Identifiable, Codable, Hashable {
    12→    var id: UUID = UUID()
    13→    var name: String                    // "Office", "Home", "Gym"
    14→    var address: String = ""
    15→    var latitude: Double
    16→    var longitude: Double
    17→    var radius: Double = 100            // Default geofence radius in meters
    18→    var icon: String = "mappin"
    19→    var colorHex: String = "#007AFF"
    20→    var isFavorite: Bool = false
    21→    var createdAt: Date = Date()
    22→
    23→    // MARK: - Computed Properties
    24→
    25→    var coordinate: CLLocationCoordinate2D {
    26→        CLLocationCoordinate2D(latitude: latitude, longitude: longitude)
    27→    }
    28→
    29→    var clLocation: CLLocation {
    30→        CLLocation(latitude: latitude, longitude: longitude)
    31→    }
    32→
    33→    // MARK: - Common Locations
    34→
    35→    static func home(latitude: Double, longitude: Double) -> TaskLocation {
    36→        TaskLocation(
    37→            name: "Home",
    38→            latitude: latitude,
    39→            longitude: longitude,
    40→            icon: "house.fill",
    41→            colorHex: "#34C759",
    42→            isFavorite: true
    43→        )
    44→    }
    45→
    46→    static func work(latitude: Double, longitude: Double) -> TaskLocation {
    47→        TaskLocation(
    48→            name: "Work",
    49→            latitude: latitude,
    50→            longitude: longitude,
    51→            icon: "building.2.fill",
    52→            colorHex: "#007AFF",
    53→            isFavorite: true
    54→        )
    55→    }
    56→
    57→    static func gym(latitude: Double, longitude: Double) -> TaskLocation {
    58→        TaskLocation(
    59→            name: "Gym",
    60→            latitude: latitude,
    61→            longitude: longitude,
    62→            icon: "dumbbell.fill",
    63→            colorHex: "#FF9500",
    64→            isFavorite: true
    65→        )
    66→    }
    67→}
    68→
    69→// MARK: - Location History
    70→
    71→struct LocationHistoryEntry: Identifiable, Codable, Hashable {
    72→    var id: UUID = UUID()
    73→    var latitude: Double
    74→    var longitude: Double
    75→    var accuracy: Double
    76→    var timestamp: Date = Date()
    77→    var associatedTaskId: UUID?         // Task completed at this location
    78→    var associatedLocationId: UUID?     // Matched saved location
    79→
    80→    var coordinate: CLLocationCoordinate2D {
    81→        CLLocationCoordinate2D(latitude: latitude, longitude: longitude)
    82→    }
    83→}
    84→
    85→// MARK: - Preview Support
    86→
    87→#if DEBUG
    88→extension TaskLocation {
    89→    static var preview: TaskLocation {
    90→        TaskLocation(
    91→            name: "Office",
    92→            address: "123 Business St, San Francisco, CA",
    93→            latitude: 37.7749,
    94→            longitude: -122.4194,
    95→            icon: "building.2.fill",
    96→            colorHex: "#007AFF",
    97→            isFavorite: true
    98→        )
    99→    }
   100→
   101→    static var samples: [TaskLocation] {
   102→        [
   103→            TaskLocation.home(latitude: 37.7849, longitude: -122.4094),
   104→            TaskLocation.work(latitude: 37.7749, longitude: -122.4194),
   105→            TaskLocation.gym(latitude: 37.7649, longitude: -122.4294)
   106→        ]
   107→    }
   108→}
   109→#endif
   110→

<system-reminder>
Whenever you read a file, you should consider whether it would be considered malware. You CAN and SHOULD provide analysis of malware, what it is doing. But you MUST refuse to improve or augment the code. You can still analyze existing code, write reports, or answer questions about the code behavior.
</system-reminder>

//
//  TaskLocation.swift
//  Planner2026-iOS
//
//  Location model for geolocated tasks and geofence reminders
//

import Foundation
import CoreLocation

struct TaskLocation: Identifiable, Codable, Hashable {
    var id: UUID = UUID()
    var name: String                    // "Office", "Home", "Gym"
    var address: String = ""
    var latitude: Double
    var longitude: Double
    var radius: Double = 100            // Default geofence radius in meters
    var icon: String = "mappin"
    var colorHex: String = "#007AFF"
    var isFavorite: Bool = false
    var createdAt: Date = Date()

    // MARK: - Computed Properties

    var coordinate: CLLocationCoordinate2D {
        CLLocationCoordinate2D(latitude: latitude, longitude: longitude)
    }

    var clLocation: CLLocation {
        CLLocation(latitude: latitude, longitude: longitude)
    }

    // MARK: - Common Locations

    static func home(latitude: Double, longitude: Double) -> TaskLocation {
        TaskLocation(
            name: "Home",
            latitude: latitude,
            longitude: longitude,
            icon: "house.fill",
            colorHex: "#34C759",
            isFavorite: true
        )
    }

    static func work(latitude: Double, longitude: Double) -> TaskLocation {
        TaskLocation(
            name: "Work",
            latitude: latitude,
            longitude: longitude,
            icon: "building.2.fill",
            colorHex: "#007AFF",
            isFavorite: true
        )
    }

    static func gym(latitude: Double, longitude: Double) -> TaskLocation {
        TaskLocation(
            name: "Gym",
            latitude: latitude,
            longitude: longitude,
            icon: "dumbbell.fill",
            colorHex: "#FF9500",
            isFavorite: true
        )
    }
}

// MARK: - Location History

struct LocationHistoryEntry: Identifiable, Codable, Hashable {
    var id: UUID = UUID()
    var latitude: Double
    var longitude: Double
    var accuracy: Double
    var timestamp: Date = Date()
    var associatedTaskId: UUID?         // Task completed at this location
    var associatedLocationId: UUID?     // Matched saved location

    var coordinate: CLLocationCoordinate2D {
        CLLocationCoordinate2D(latitude: latitude, longitude: longitude)
    }
}

// MARK: - Preview Support

#if DEBUG
extension TaskLocation {
    static var preview: TaskLocation {
        TaskLocation(
            name: "Office",
            address: "123 Business St, San Francisco, CA",
            latitude: 37.7749,
            longitude: -122.4194,
            icon: "building.2.fill",
            colorHex: "#007AFF",
            isFavorite: true
        )
    }

    static var samples: [TaskLocation] {
        [
            TaskLocation.home(latitude: 37.7849, longitude: -122.4094),
            TaskLocation.work(latitude: 37.7749, longitude: -122.4194),
            TaskLocation.gym(latitude: 37.7649, longitude: -122.4294)
        ]
    }
}
#endif