Explore /Users/neog/Apps/macOS/NEOG-Finance and identify:

1. Code duplication patterns across files
2. Common patterns that could be extracted into reusable components
3. Views that share similar UI patterns
4. Services/Managers that have overlapping responsibilities
5. Extensions that could be consolidated

Look for opportunities to reduce code duplication and improve reusability.


I'll explore the NEOG-Finance macOS app to identify code duplication patterns and opportunities for consolidation.


Bash

find /Users/neog/Apps/macOS/NEOG-Finance -type f -name "*.swift" -o -name "*.h" -o -name "*.m" | head -50


Let me continue exploring the structure and then read key files:


find /Users/neog/Apps/macOS/NEOG-Finance -type f \( -name "*.swift" -o -name "*.h" -o -name "*.m" \) | wc -l


Now let me read the key files systematically to identify patterns:


     1→import SwiftUI
     2→
     3→// MARK: - Enhanced Stat Card
     4→struct EnhancedStatCard: View {
     5→    let title: String
     6→    let value: String
     7→    let subtitle: String?
     8→    let icon: String
     9→    let color: Color
    10→    var trend: Trend?
    11→    var isCompact: Bool = false
    12→
    13→    enum Trend {
    14→        case up(String)
    15→        case down(String)
    16→        case neutral(String)
    17→
    18→        var color: Color {
    19→            switch self {
    20→            case .up: return AppTheme.Colors.success
    21→            case .down: return AppTheme.Colors.danger
    22→            case .neutral: return AppTheme.Colors.textSecondary
    23→            }
    24→        }
    25→
    26→        var icon: String {
    27→            switch self {
    28→            case .up: return "arrow.up.right"
    29→            case .down: return "arrow.down.right"
    30→            case .neutral: return "arrow.right"
    31→            }
    32→        }
    33→
    34→        var text: String {
    35→            switch self {
    36→            case .up(let text), .down(let text), .neutral(let text):
    37→                return text
    38→            }
    39→        }
    40→    }
    41→
    42→    @State private var isHovered = false
    43→
    44→    var body: some View {
    45→        VStack(alignment: .leading, spacing: isCompact ? AppTheme.Spacing.sm : AppTheme.Spacing.md) {
    46→            // Header
    47→            HStack {
    48→                // Icon
    49→                ZStack {
    50→                    Circle()
    51→                        .fill(color.opacity(0.15))
    52→                        .frame(width: isCompact ? 32 : 40, height: isCompact ? 32 : 40)
    53→
    54→                    Image(systemName: icon)
    55→                        .font(isCompact ? .body : .title3)
    56→                        .foregroundColor(color)
    57→                }
    58→
    59→                Spacer()
    60→
    61→                // Trend indicator
    62→                if let trend = trend {
    63→                    HStack(spacing: 4) {
    64→                        Image(systemName: trend.icon)
    65→                            .font(AppTheme.Typography.caption)
    66→                        Text(trend.text)
    67→                            .font(AppTheme.Typography.caption)
    68→                    }
    69→                    .foregroundColor(trend.color)
    70→                    .padding(.horizontal, 8)
    71→                    .padding(.vertical, 4)
    72→                    .background(
    73→                        Capsule()
    74→                            .fill(trend.color.opacity(0.1))
    75→                    )
    76→                }
    77→            }
    78→
    79→            // Value
    80→            VStack(alignment: .leading, spacing: AppTheme.Spacing.xs) {
    81→                Text(value)
    82→                    .font(isCompact ? AppTheme.Typography.title3 : AppTheme.Typography.title2)
    83→                    .fontWeight(.bold)
    84→                    .foregroundColor(AppTheme.Colors.textPrimary)
    85→                    .lineLimit(1)
    86→                    .minimumScaleFactor(0.8)
    87→
    88→                Text(title)
    89→                    .font(isCompact ? AppTheme.Typography.caption : AppTheme.Typography.subheadline)
    90→                    .foregroundColor(AppTheme.Colors.textSecondary)
    91→
    92→                if let subtitle = subtitle {
    93→                    Text(subtitle)
    94→                        .font(AppTheme.Typography.caption)
    95→                        .foregroundColor(AppTheme.Colors.textTertiary)
    96→                }
    97→            }
    98→        }
    99→        .padding(isCompact ? AppTheme.Spacing.md : AppTheme.Spacing.lg)
   100→        .frame(maxWidth: .infinity, alignment: .leading)
   101→        .background(
   102→            RoundedRectangle(cornerRadius: AppTheme.Radius.large)
   103→                .fill(AppTheme.Colors.cardBackground)
   104→                .shadow(color: isHovered ? color.opacity(0.2) : .clear, radius: 8, x: 0, y: 4)
   105→        )
   106→        .overlay(
   107→            RoundedRectangle(cornerRadius: AppTheme.Radius.large)
   108→                .stroke(isHovered ? color.opacity(0.3) : .clear, lineWidth: 1)
   109→        )
   110→        .scaleEffect(isHovered ? 1.02 : 1.0)
   111→        .animation(AppTheme.Animation.spring, value: isHovered)
   112→        .onHover { hovering in
   113→            isHovered = hovering
   114→        }
   115→    }
   116→}
   117→
   118→// MARK: - Section Header
   119→struct SectionHeader: View {
   120→    let title: String
   121→    var subtitle: String?
   122→    var icon: String?
   123→    var action: (() -> Void)?
   124→    var actionLabel: String?
   125→
   126→    var body: some View {
   127→        HStack(alignment: .center, spacing: AppTheme.Spacing.md) {
   128→            if let icon = icon {
   129→                Image(systemName: icon)
   130→                    .font(.title3)
   131→                    .foregroundColor(AppTheme.Colors.primary)
   132→            }
   133→
   134→            VStack(alignment: .leading, spacing: 2) {
   135→                Text(title)
   136→                    .font(AppTheme.Typography.headline)
   137→                    .foregroundColor(AppTheme.Colors.textPrimary)
   138→
   139→                if let subtitle = subtitle {
   140→                    Text(subtitle)
   141→                        .font(AppTheme.Typography.caption)
   142→                        .foregroundColor(AppTheme.Colors.textSecondary)
   143→                }
   144→            }
   145→
   146→            Spacer()
   147→
   148→            if let action = action, let label = actionLabel {
   149→                Button(action: action) {
   150→                    HStack(spacing: 4) {
   151→                        Text(label)
   152→                        Image(systemName: "chevron.right")
   153→                    }
   154→                    .font(AppTheme.Typography.caption)
   155→                    .foregroundColor(AppTheme.Colors.primary)
   156→                }
   157→                .buttonStyle(.plain)
   158→            }
   159→        }
   160→    }
   161→}
   162→
   163→// MARK: - Empty State View
   164→struct EmptyStateView: View {
   165→    let icon: String
   166→    let title: String
   167→    let description: String
   168→    var actionTitle: String?
   169→    var action: (() -> Void)?
   170→    var color: Color = AppTheme.Colors.textSecondary
   171→
   172→    var body: some View {
   173→        VStack(spacing: AppTheme.Spacing.lg) {
   174→            // Animated Icon
   175→            ZStack {
   176→                Circle()
   177→                    .fill(color.opacity(0.1))
   178→                    .frame(width: 100, height: 100)
   179→
   180→                Circle()
   181→                    .stroke(color.opacity(0.2), lineWidth: 2)
   182→                    .frame(width: 120, height: 120)
   183→
   184→                Image(systemName: icon)
   185→                    .font(.system(size: 40))
   186→                    .foregroundColor(color)
   187→            }
   188→            .padding(.bottom, AppTheme.Spacing.md)
   189→
   190→            Text(title)
   191→                .font(AppTheme.Typography.title3)
   192→                .foregroundColor(AppTheme.Colors.textPrimary)
   193→
   194→            Text(description)
   195→                .font(AppTheme.Typography.body)
   196→                .foregroundColor(AppTheme.Colors.textSecondary)
   197→                .multilineTextAlignment(.center)
   198→                .frame(maxWidth: 300)
   199→
   200→            if let actionTitle = actionTitle, let action = action {
   201→                Button(action: action) {
   202→                    Label(actionTitle, systemImage: "plus.circle.fill")
   203→                }
   204→                .buttonStyle(PrimaryButtonStyle())
   205→                .padding(.top, AppTheme.Spacing.md)
   206→            }
   207→        }
   208→        .frame(maxWidth: .infinity, maxHeight: .infinity)
   209→        .padding(AppTheme.Spacing.xxl)
   210→    }
   211→}
   212→
   213→// MARK: - Quick Action Button
   214→struct QuickActionButton: View {
   215→    let icon: String
   216→    let title: String
   217→    let color: Color
   218→    let action: () -> Void
   219→
   220→    @State private var isHovered = false
   221→    @State private var isPressed = false
   222→
   223→    var body: some View {
   224→        Button(action: action) {
   225→            VStack(spacing: AppTheme.Spacing.sm) {
   226→                ZStack {
   227→                    Circle()
   228→                        .fill(color.opacity(isHovered ? 0.2 : 0.1))
   229→                        .frame(width: 48, height: 48)
   230→
   231→                    Image(systemName: icon)
   232→                        .font(.title3)
   233→                        .foregroundColor(color)
   234→                }
   235→
   236→                Text(title)
   237→                    .font(AppTheme.Typography.caption)
   238→                    .foregroundColor(AppTheme.Colors.textSecondary)
   239→            }
   240→        }
   241→        .buttonStyle(.plain)
   242→        .scaleEffect(isPressed ? 0.95 : (isHovered ? 1.05 : 1.0))
   243→        .animation(AppTheme.Animation.quick, value: isHovered)
   244→        .animation(AppTheme.Animation.quick, value: isPressed)
   245→        .onHover { hovering in
   246→            isHovered = hovering
   247→        }
   248→    }
   249→}
   250→
   251→// MARK: - Info Row
   252→struct InfoRow: View {
   253→    let label: String
   254→    let value: String
   255→    var valueColor: Color = AppTheme.Colors.textPrimary
   256→    var icon: String?
   257→
   258→    var body: some View {
   259→        HStack {
   260→            if let icon = icon {
   261→                Image(systemName: icon)
   262→                    .font(AppTheme.Typography.caption)
   263→                    .foregroundColor(AppTheme.Colors.textTertiary)
   264→                    .frame(width: 20)
   265→            }
   266→
   267→            Text(label)
   268→                .font(AppTheme.Typography.callout)
   269→                .foregroundColor(AppTheme.Colors.textSecondary)
   270→
   271→            Spacer()
   272→
   273→            Text(value)
   274→                .font(AppTheme.Typography.callout)
   275→                .fontWeight(.medium)
   276→                .foregroundColor(valueColor)
   277→        }
   278→    }
   279→}
   280→
   281→// MARK: - Metric Card
   282→struct MetricCard: View {
   283→    let title: String
   284→    let value: String
   285→    let icon: String
   286→    let color: Color
   287→    var progress: Double?
   288→
   289→    var body: some View {
   290→        HStack(spacing: AppTheme.Spacing.md) {
   291→            // Icon
   292→            ZStack {
   293→                RoundedRectangle(cornerRadius: AppTheme.Radius.small)
   294→                    .fill(color.opacity(0.15))
   295→                    .frame(width: 36, height: 36)
   296→
   297→                Image(systemName: icon)
   298→                    .font(.body)
   299→                    .foregroundColor(color)
   300→            }
   301→
   302→            VStack(alignment: .leading, spacing: 2) {
   303→                Text(title)
   304→                    .font(AppTheme.Typography.caption)
   305→                    .foregroundColor(AppTheme.Colors.textSecondary)
   306→
   307→                Text(value)
   308→                    .font(AppTheme.Typography.callout)
   309→                    .fontWeight(.semibold)
   310→                    .foregroundColor(AppTheme.Colors.textPrimary)
   311→            }
   312→
   313→            if let progress = progress {
   314→                Spacer()
   315→
   316→                ProgressRing(
   317→                    progress: progress,
   318→                    lineWidth: 4,
   319→                    color: color,
   320→                    showPercentage: false
   321→                )
   322→                .frame(width: 32, height: 32)
   323→            }
   324→        }
   325→        .padding(AppTheme.Spacing.md)
   326→        .background(AppTheme.Colors.cardBackground)
   327→        .cornerRadius(AppTheme.Radius.medium)
   328→    }
   329→}
   330→
   331→// MARK: - Alert Banner
   332→struct AlertBanner: View {
   333→    enum AlertType {
   334→        case info, success, warning, danger
   335→
   336→        var color: Color {
   337→            switch self {
   338→            case .info: return AppTheme.Colors.info
   339→            case .success: return AppTheme.Colors.success
   340→            case .warning: return AppTheme.Colors.warning
   341→            case .danger: return AppTheme.Colors.danger
   342→            }
   343→        }
   344→
   345→        var icon: String {
   346→            switch self {
   347→            case .info: return "info.circle.fill"
   348→            case .success: return "checkmark.circle.fill"
   349→            case .warning: return "exclamationmark.triangle.fill"
   350→            case .danger: return "xmark.circle.fill"
   351→            }
   352→        }
   353→    }
   354→
   355→    let type: AlertType
   356→    let title: String
   357→    var message: String?
   358→    var action: (() -> Void)?
   359→    var actionLabel: String?
   360→
   361→    @State private var isVisible = true
   362→
   363→    var body: some View {
   364→        if isVisible {
   365→            HStack(spacing: AppTheme.Spacing.md) {
   366→                Image(systemName: type.icon)
   367→                    .font(.title3)
   368→                    .foregroundColor(type.color)
   369→
   370→                VStack(alignment: .leading, spacing: 2) {
   371→                    Text(title)
   372→                        .font(AppTheme.Typography.callout)
   373→                        .fontWeight(.semibold)
   374→                        .foregroundColor(AppTheme.Colors.textPrimary)
   375→
   376→                    if let message = message {
   377→                        Text(message)
   378→                            .font(AppTheme.Typography.caption)
   379→                            .foregroundColor(AppTheme.Colors.textSecondary)
   380→                    }
   381→                }
   382→
   383→                Spacer()
   384→
   385→                if let action = action, let label = actionLabel {
   386→                    Button(action: action) {
   387→                        Text(label)
   388→                            .font(AppTheme.Typography.caption)
   389→                            .fontWeight(.medium)
   390→                    }
   391→                    .buttonStyle(SecondaryButtonStyle(color: type.color))
   392→                }
   393→
   394→                Button {
   395→                    withAnimation(AppTheme.Animation.standard) {
   396→                        isVisible = false
   397→                    }
   398→                } label: {
   399→                    Image(systemName: "xmark")
   400→                        .font(AppTheme.Typography.caption)
   401→                        .foregroundColor(AppTheme.Colors.textTertiary)
   402→                }
   403→                .buttonStyle(.plain)
   404→            }
   405→            .padding(AppTheme.Spacing.md)
   406→            .background(
   407→                RoundedRectangle(cornerRadius: AppTheme.Radius.medium)
   408→                    .fill(type.color.opacity(0.1))
   409→            )
   410→            .overlay(
   411→                RoundedRectangle(cornerRadius: AppTheme.Radius.medium)
   412→                    .stroke(type.color.opacity(0.3), lineWidth: 1)
   413→            )
   414→            .transition(.move(edge: .top).combined(with: .opacity))
   415→        }
   416→    }
   417→}
   418→
   419→// MARK: - Tooltip View
   420→struct TooltipModifier: ViewModifier {
   421→    let text: String
   422→    @State private var isShowing = false
   423→
   424→    func body(content: Content) -> some View {
   425→        content
   426→            .overlay(alignment: .top) {
   427→                if isShowing {
   428→                    Text(text)
   429→                        .font(AppTheme.Typography.caption)
   430→                        .foregroundColor(.white)
   431→                        .padding(.horizontal, AppTheme.Spacing.sm)
   432→                        .padding(.vertical, AppTheme.Spacing.xs)
   433→                        .background(
   434→                            RoundedRectangle(cornerRadius: AppTheme.Radius.small)
   435→                                .fill(Color.black.opacity(0.8))
   436→                        )
   437→                        .offset(y: -30)
   438→                        .transition(.opacity.combined(with: .scale))
   439→                }
   440→            }
   441→            .onHover { hovering in
   442→                withAnimation(AppTheme.Animation.quick) {
   443→                    isShowing = hovering
   444→                }
   445→            }
   446→    }
   447→}
   448→
   449→extension View {
   450→    func tooltip(_ text: String) -> some View {
   451→        modifier(TooltipModifier(text: text))
   452→    }
   453→}
   454→
   455→// MARK: - Divider with Label
   456→struct LabeledDivider: View {
   457→    let label: String
   458→
   459→    var body: some View {
   460→        HStack {
   461→            Rectangle()
   462→                .fill(AppTheme.Colors.textTertiary.opacity(0.3))
   463→                .frame(height: 1)
   464→
   465→            Text(label)
   466→                .font(AppTheme.Typography.caption)
   467→                .foregroundColor(AppTheme.Colors.textTertiary)
   468→                .padding(.horizontal, AppTheme.Spacing.sm)
   469→
   470→            Rectangle()
   471→                .fill(AppTheme.Colors.textTertiary.opacity(0.3))
   472→                .frame(height: 1)
   473→        }
   474→    }
   475→}
   476→
   477→#Preview("Stat Cards") {
   478→    HStack(spacing: 16) {
   479→        EnhancedStatCard(
   480→            title: "Total Dividas",
   481→            value: "R$ 23.300",
   482→            subtitle: "7 pendentes",
   483→            icon: "creditcard.fill",
   484→            color: .red,
   485→            trend: .down("-15%")
   486→        )
   487→
   488→        EnhancedStatCard(
   489→            title: "Receita Mensal",
   490→            value: "R$ 11.000",
   491→            subtitle: "Freelance + LOAS",
   492→            icon: "arrow.down.circle.fill",
   493→            color: .green,
   494→            trend: .up("+8%")
   495→        )
   496→    }
   497→    .padding()
   498→    .frame(width: 600)
   499→}
   500→
   501→#Preview("Empty State") {
   502→    EmptyStateView(
   503→        icon: "doc.text.magnifyingglass",
   504→        title: "Nenhuma transacao",
   505→        description: "Adicione sua primeira transacao para comecar a acompanhar suas financas.",
   506→        actionTitle: "Adicionar",
   507→        action: {}
   508→    )
   509→    .frame(width: 500, height: 400)
   510→}
   511→

<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>

import SwiftUI

// MARK: - Enhanced Stat Card
struct EnhancedStatCard: View {
    let title: String
    let value: String
    let subtitle: String?
    let icon: String
    let color: Color
    var trend: Trend?
    var isCompact: Bool = false

    enum Trend {
        case up(String)
        case down(String)
        case neutral(String)

        var color: Color {
            switch self {
            case .up: return AppTheme.Colors.success
            case .down: return AppTheme.Colors.danger
            case .neutral: return AppTheme.Colors.textSecondary
            }
        }

        var icon: String {
            switch self {
            case .up: return "arrow.up.right"
            case .down: return "arrow.down.right"
            case .neutral: return "arrow.right"
            }
        }

        var text: String {
            switch self {
            case .up(let text), .down(let text), .neutral(let text):
                return text
            }
        }
    }

    @State private var isHovered = false

    var body: some View {
        VStack(alignment: .leading, spacing: isCompact ? AppTheme.Spacing.sm : AppTheme.Spacing.md) {
            // Header
            HStack {
                // Icon
                ZStack {
                    Circle()
                        .fill(color.opacity(0.15))
                        .frame(width: isCompact ? 32 : 40, height: isCompact ? 32 : 40)

                    Image(systemName: icon)
                        .font(isCompact ? .body : .title3)
                        .foregroundColor(color)
                }

                Spacer()

                // Trend indicator
                if let trend = trend {
                    HStack(spacing: 4) {
                        Image(systemName: trend.icon)
                            .font(AppTheme.Typography.caption)
                        Text(trend.text)
                            .font(AppTheme.Typography.caption)
                    }
                    .foregroundColor(trend.color)
                    .padding(.horizontal, 8)
                    .padding(.vertical, 4)
                    .background(
                        Capsule()
                            .fill(trend.color.opacity(0.1))
                    )
                }
            }

            // Value
            VStack(alignment: .leading, spacing: AppTheme.Spacing.xs) {
                Text(value)
                    .font(isCompact ? AppTheme.Typography.title3 : AppTheme.Typography.title2)
                    .fontWeight(.bold)
                    .foregroundColor(AppTheme.Colors.textPrimary)
                    .lineLimit(1)
                    .minimumScaleFactor(0.8)

                Text(title)
                    .font(isCompact ? AppTheme.Typography.caption : AppTheme.Typography.subheadline)
                    .foregroundColor(AppTheme.Colors.textSecondary)

                if let subtitle = subtitle {
                    Text(subtitle)
                        .font(AppTheme.Typography.caption)
                        .foregroundColor(AppTheme.Colors.textTertiary)
                }
            }
        }
        .padding(isCompact ? AppTheme.Spacing.md : AppTheme.Spacing.lg)
        .frame(maxWidth: .infinity, alignment: .leading)
        .background(
            RoundedRectangle(cornerRadius: AppTheme.Radius.large)
                .fill(AppTheme.Colors.cardBackground)
                .shadow(color: isHovered ? color.opacity(0.2) : .clear, radius: 8, x: 0, y: 4)
        )
        .overlay(
            RoundedRectangle(cornerRadius: AppTheme.Radius.large)
                .stroke(isHovered ? color.opacity(0.3) : .clear, lineWidth: 1)
        )
        .scaleEffect(isHovered ? 1.02 : 1.0)
        .animation(AppTheme.Animation.spring, value: isHovered)
        .onHover { hovering in
            isHovered = hovering
        }
    }
}

// MARK: - Section Header
struct SectionHeader: View {
    let title: String
    var subtitle: String?
    var icon: String?
    var action: (() -> Void)?
    var actionLabel: String?

    var body: some View {
        HStack(alignment: .center, spacing: AppTheme.Spacing.md) {
            if let icon = icon {
                Image(systemName: icon)
                    .font(.title3)
                    .foregroundColor(AppTheme.Colors.primary)
            }

            VStack(alignment: .leading, spacing: 2) {
                Text(title)
                    .font(AppTheme.Typography.headline)
                    .foregroundColor(AppTheme.Colors.textPrimary)

                if let subtitle = subtitle {
                    Text(subtitle)
                        .font(AppTheme.Typography.caption)
                        .foregroundColor(AppTheme.Colors.textSecondary)
                }
            }

            Spacer()

            if let action = action, let label = actionLabel {
                Button(action: action) {
                    HStack(spacing: 4) {
                        Text(label)
                        Image(systemName: "chevron.right")
                    }
                    .font(AppTheme.Typography.caption)
                    .foregroundColor(AppTheme.Colors.primary)
                }
                .buttonStyle(.plain)
            }
        }
    }
}

// MARK: - Empty State View
struct EmptyStateView: View {
    let icon: String
    let title: String
    let description: String
    var actionTitle: String?
    var action: (() -> Void)?
    var color: Color = AppTheme.Colors.textSecondary

    var body: some View {
        VStack(spacing: AppTheme.Spacing.lg) {
            // Animated Icon
            ZStack {
                Circle()
                    .fill(color.opacity(0.1))
                    .frame(width: 100, height: 100)

                Circle()
                    .stroke(color.opacity(0.2), lineWidth: 2)
                    .frame(width: 120, height: 120)

                Image(systemName: icon)
                    .font(.system(size: 40))
                    .foregroundColor(color)
            }
            .padding(.bottom, AppTheme.Spacing.md)

            Text(title)
                .font(AppTheme.Typography.title3)
                .foregroundColor(AppTheme.Colors.textPrimary)

            Text(description)
                .font(AppTheme.Typography.body)
                .foregroundColor(AppTheme.Colors.textSecondary)
                .multilineTextAlignment(.center)
                .frame(maxWidth: 300)

            if let actionTitle = actionTitle, let action = action {
                Button(action: action) {
                    Label(actionTitle, systemImage: "plus.circle.fill")
                }
                .buttonStyle(PrimaryButtonStyle())
                .padding(.top, AppTheme.Spacing.md)
            }
        }
        .frame(maxWidth: .infinity, maxHeight: .infinity)
        .padding(AppTheme.Spacing.xxl)
    }
}

// MARK: - Quick Action Button
struct QuickActionButton: View {
    let icon: String
    let title: String
    let color: Color
    let action: () -> Void

    @State private var isHovered = false
    @State private var isPressed = false

    var body: some View {
        Button(action: action) {
            VStack(spacing: AppTheme.Spacing.sm) {
                ZStack {
                    Circle()
                        .fill(color.opacity(isHovered ? 0.2 : 0.1))
                        .frame(width: 48, height: 48)

                    Image(systemName: icon)
                        .font(.title3)
                        .foregroundColor(color)
                }

                Text(title)
                    .font(AppTheme.Typography.caption)
                    .foregroundColor(AppTheme.Colors.textSecondary)
            }
        }
        .buttonStyle(.plain)
        .scaleEffect(isPressed ? 0.95 : (isHovered ? 1.05 : 1.0))
        .animation(AppTheme.Animation.quick, value: isHovered)
        .animation(AppTheme.Animation.quick, value: isPressed)
        .onHover { hovering in
            isHovered = hovering
        }
    }
}

// MARK: - Info Row
struct InfoRow: View {
    let label: String
    let value: String
    var valueColor: Color = AppTheme.Colors.textPrimary
    var icon: String?

    var body: some View {
        HStack {
            if let icon = icon {
                Image(systemName: icon)
                    .font(AppTheme.Typography.caption)
                    .foregroundColor(AppTheme.Colors.textTertiary)
                    .frame(width: 20)
            }

            Text(label)
                .font(AppTheme.Typography.callout)
                .foregroundColor(AppTheme.Colors.textSecondary)

            Spacer()

            Text(value)
                .font(AppTheme.Typography.callout)
                .fontWeight(.medium)
                .foregroundColor(valueColor)
        }
    }
}

// MARK: - Metric Card
struct MetricCard: View {
    let title: String
    let value: String
    let icon: String
    let color: Color
    var progress: Double?

    var body: some View {
        HStack(spacing: AppTheme.Spacing.md) {
            // Icon
            ZStack {
                RoundedRectangle(cornerRadius: AppTheme.Radius.small)
                    .fill(color.opacity(0.15))
                    .frame(width: 36, height: 36)

                Image(systemName: icon)
                    .font(.body)
                    .foregroundColor(color)
            }

            VStack(alignment: .leading, spacing: 2) {
                Text(title)
                    .font(AppTheme.Typography.caption)
                    .foregroundColor(AppTheme.Colors.textSecondary)

                Text(value)
                    .font(AppTheme.Typography.callout)
                    .fontWeight(.semibold)
                    .foregroundColor(AppTheme.Colors.textPrimary)
            }

            if let progress = progress {
                Spacer()

                ProgressRing(
                    progress: progress,
                    lineWidth: 4,
                    color: color,
                    showPercentage: false
                )
                .frame(width: 32, height: 32)
            }
        }
        .padding(AppTheme.Spacing.md)
        .background(AppTheme.Colors.cardBackground)
        .cornerRadius(AppTheme.Radius.medium)
    }
}

// MARK: - Alert Banner
struct AlertBanner: View {
    enum AlertType {
        case info, success, warning, danger

        var color: Color {
            switch self {
            case .info: return AppTheme.Colors.info
            case .success: return AppTheme.Colors.success
            case .warning: return AppTheme.Colors.warning
            case .danger: return AppTheme.Colors.danger
            }
        }

        var icon: String {
            switch self {
            case .info: return "info.circle.fill"
            case .success: return "checkmark.circle.fill"
            case .warning: return "exclamationmark.triangle.fill"
            case .danger: return "xmark.circle.fill"
            }
        }
    }

    let type: AlertType
    let title: String
    var message: String?
    var action: (() -> Void)?
    var actionLabel: String?

    @State private var isVisible = true

    var body: some View {
        if isVisible {
            HStack(spacing: AppTheme.Spacing.md) {
                Image(systemName: type.icon)
                    .font(.title3)
                    .foregroundColor(type.color)

                VStack(alignment: .leading, spacing: 2) {
                    Text(title)
                        .font(AppTheme.Typography.callout)
                        .fontWeight(.semibold)
                        .foregroundColor(AppTheme.Colors.textPrimary)

                    if let message = message {
                        Text(message)
                            .font(AppTheme.Typography.caption)
                            .foregroundColor(AppTheme.Colors.textSecondary)
                    }
                }

                Spacer()

                if let action = action, let label = actionLabel {
                    Button(action: action) {
                        Text(label)
                            .font(AppTheme.Typography.caption)
                            .fontWeight(.medium)
                    }
                    .buttonStyle(SecondaryButtonStyle(color: type.color))
                }

                Button {
                    withAnimation(AppTheme.Animation.standard) {
                        isVisible = false
                    }
                } label: {
                    Image(systemName: "xmark")
                        .font(AppTheme.Typography.caption)
                        .foregroundColor(AppTheme.Colors.textTertiary)
                }
                .buttonStyle(.plain)
            }
            .padding(AppTheme.Spacing.md)
            .background(
                RoundedRectangle(cornerRadius: AppTheme.Radius.medium)
                    .fill(type.color.opacity(0.1))
            )
            .overlay(
                RoundedRectangle(cornerRadius: AppTheme.Radius.medium)
                    .stroke(type.color.opacity(0.3), lineWidth: 1)
            )
            .transition(.move(edge: .top).combined(with: .opacity))
        }
    }
}

// MARK: - Tooltip View
struct TooltipModifier: ViewModifier {
    let text: String
    @State private var isShowing = false

    func body(content: Content) -> some View {
        content
            .overlay(alignment: .top) {
                if isShowing {
                    Text(text)
                        .font(AppTheme.Typography.caption)
                        .foregroundColor(.white)
                        .padding(.horizontal, AppTheme.Spacing.sm)
                        .padding(.vertical, AppTheme.Spacing.xs)
                        .background(
                            RoundedRectangle(cornerRadius: AppTheme.Radius.small)
                                .fill(Color.black.opacity(0.8))
                        )
                        .offset(y: -30)
                        .transition(.opacity.combined(with: .scale))
                }
            }
            .onHover { hovering in
                withAnimation(AppTheme.Animation.quick) {
                    isShowing = hovering
                }
            }
    }
}

extension View {
    func tooltip(_ text: String) -> some View {
        modifier(TooltipModifier(text: text))
    }
}

// MARK: - Divider with Label
struct LabeledDivider: View {
    let label: String

    var body: some View {
        HStack {
            Rectangle()
                .fill(AppTheme.Colors.textTertiary.opacity(0.3))
                .frame(height: 1)

            Text(label)
                .font(AppTheme.Typography.caption)
                .foregroundColor(AppTheme.Colors.textTertiary)
                .padding(.horizontal, AppTheme.Spacing.sm)

            Rectangle()
                .fill(AppTheme.Colors.textTertiary.opacity(0.3))
                .frame(height: 1)
        }
    }
}

#Preview("Stat Cards") {
    HStack(spacing: 16) {
        EnhancedStatCard(
            title: "Total Dividas",
            value: "R$ 23.300",
            subtitle: "7 pendentes",
            icon: "creditcard.fill",
            color: .red,
            trend: .down("-15%")
        )

        EnhancedStatCard(
            title: "Receita Mensal",
            value: "R$ 11.000",
            subtitle: "Freelance + LOAS",
            icon: "arrow.down.circle.fill",
            color: .green,
            trend: .up("+8%")
        )
    }
    .padding()
    .frame(width: 600)
}

#Preview("Empty State") {
    EmptyStateView(
        icon: "doc.text.magnifyingglass",
        title: "Nenhuma transacao",
        description: "Adicione sua primeira transacao para comecar a acompanhar suas financas.",
        actionTitle: "Adicionar",
        action: {}
    )
    .frame(width: 500, height: 400)
}


     1→import SwiftUI
     2→
     3→// MARK: - AI Insight Card
     4→struct AIInsightCard: View {
     5→    let insight: ClaudeAIService.QuickInsight
     6→
     7→    @State private var isHovered = false
     8→
     9→    var body: some View {
    10→        HStack(spacing: AppTheme.Spacing.md) {
    11→            // Icon
    12→            ZStack {
    13→                Circle()
    14→                    .fill(insight.type.color.opacity(0.15))
    15→                    .frame(width: 40, height: 40)
    16→
    17→                Image(systemName: insight.type.icon)
    18→                    .font(.body)
    19→                    .foregroundColor(insight.type.color)
    20→            }
    21→
    22→            VStack(alignment: .leading, spacing: 4) {
    23→                Text(insight.title)
    24→                    .font(AppTheme.Typography.callout)
    25→                    .fontWeight(.semibold)
    26→                    .foregroundColor(AppTheme.Colors.textPrimary)
    27→
    28→                Text(insight.description)
    29→                    .font(AppTheme.Typography.caption)
    30→                    .foregroundColor(AppTheme.Colors.textSecondary)
    31→                    .lineLimit(2)
    32→            }
    33→
    34→            Spacer()
    35→
    36→            if insight.action != nil {
    37→                Image(systemName: "chevron.right")
    38→                    .font(.caption)
    39→                    .foregroundColor(AppTheme.Colors.textTertiary)
    40→            }
    41→        }
    42→        .padding(AppTheme.Spacing.md)
    43→        .background(
    44→            RoundedRectangle(cornerRadius: AppTheme.Radius.medium)
    45→                .fill(isHovered ? insight.type.color.opacity(0.08) : AppTheme.Colors.elevatedBackground)
    46→        )
    47→        .overlay(
    48→            RoundedRectangle(cornerRadius: AppTheme.Radius.medium)
    49→                .stroke(isHovered ? insight.type.color.opacity(0.3) : .clear, lineWidth: 1)
    50→        )
    51→        .scaleEffect(isHovered ? 1.01 : 1.0)
    52→        .animation(AppTheme.Animation.quick, value: isHovered)
    53→        .onHover { hovering in
    54→            isHovered = hovering
    55→        }
    56→    }
    57→}
    58→
    59→// MARK: - AI Assistant View (Chat Interface)
    60→struct AIAssistantView: View {
    61→    @StateObject private var aiService = ClaudeAIService.shared
    62→    @EnvironmentObject var dataManager: DataManager
    63→    @Environment(\.dismiss) private var dismiss
    64→
    65→    @State private var userInput = ""
    66→    @State private var messages: [ChatMessage] = []
    67→    @State private var isThinking = false
    68→
    69→    struct ChatMessage: Identifiable {
    70→        let id = UUID()
    71→        let role: MessageRole
    72→        let content: String
    73→        let timestamp: Date
    74→
    75→        enum MessageRole {
    76→            case user, assistant, system
    77→        }
    78→    }
    79→
    80→    var body: some View {
    81→        VStack(spacing: 0) {
    82→            // Header
    83→            headerView
    84→
    85→            Divider()
    86→
    87→            // Messages
    88→            ScrollViewReader { proxy in
    89→                ScrollView {
    90→                    LazyVStack(spacing: AppTheme.Spacing.md) {
    91→                        // Welcome message
    92→                        if messages.isEmpty {
    93→                            welcomeView
    94→                        }
    95→
    96→                        ForEach(messages) { message in
    97→                            ChatBubble(message: message)
    98→                                .id(message.id)
    99→                        }
   100→
   101→                        if isThinking {
   102→                            thinkingView
   103→                        }
   104→                    }
   105→                    .padding(AppTheme.Spacing.lg)
   106→                }
   107→                .onChange(of: messages.count) { _, _ in
   108→                    if let lastMessage = messages.last {
   109→                        withAnimation {
   110→                            proxy.scrollTo(lastMessage.id, anchor: .bottom)
   111→                        }
   112→                    }
   113→                }
   114→            }
   115→
   116→            Divider()
   117→
   118→            // Input area
   119→            inputView
   120→        }
   121→        .frame(width: 600, height: 700)
   122→        .background(AppTheme.Colors.background)
   123→    }
   124→
   125→    // MARK: - Header
   126→    private var headerView: some View {
   127→        HStack {
   128→            HStack(spacing: AppTheme.Spacing.sm) {
   129→                ZStack {
   130→                    Circle()
   131→                        .fill(
   132→                            LinearGradient(
   133→                                colors: [.cyan, .purple],
   134→                                startPoint: .topLeading,
   135→                                endPoint: .bottomTrailing
   136→                            )
   137→                        )
   138→                        .frame(width: 36, height: 36)
   139→
   140→                    Image(systemName: "brain.head.profile")
   141→                        .font(.body)
   142→                        .foregroundColor(.white)
   143→                }
   144→
   145→                VStack(alignment: .leading, spacing: 2) {
   146→                    Text("Assistente Financeiro IA")
   147→                        .font(AppTheme.Typography.headline)
   148→                        .foregroundColor(AppTheme.Colors.textPrimary)
   149→
   150→                    Text("Powered by Claude")
   151→                        .font(AppTheme.Typography.caption2)
   152→                        .foregroundColor(AppTheme.Colors.textTertiary)
   153→                }
   154→            }
   155→
   156→            Spacer()
   157→
   158→            Button {
   159→                dismiss()
   160→            } label: {
   161→                Image(systemName: "xmark.circle.fill")
   162→                    .font(.title2)
   163→                    .foregroundColor(AppTheme.Colors.textTertiary)
   164→            }
   165→            .buttonStyle(.plain)
   166→        }
   167→        .padding(AppTheme.Spacing.lg)
   168→        .background(AppTheme.Colors.cardBackground)
   169→    }
   170→
   171→    // MARK: - Welcome View
   172→    private var welcomeView: some View {
   173→        VStack(spacing: AppTheme.Spacing.lg) {
   174→            Image(systemName: "sparkles")
   175→                .font(.system(size: 50))
   176→                .foregroundStyle(
   177→                    LinearGradient(
   178→                        colors: [.cyan, .purple],
   179→                        startPoint: .topLeading,
   180→                        endPoint: .bottomTrailing
   181→                    )
   182→                )
   183→
   184→            Text("Ola! Sou seu assistente financeiro.")
   185→                .font(AppTheme.Typography.title3)
   186→                .foregroundColor(AppTheme.Colors.textPrimary)
   187→
   188→            Text("Posso ajudar com analises, dicas e planejamento financeiro baseado nos seus dados.")
   189→                .font(AppTheme.Typography.body)
   190→                .foregroundColor(AppTheme.Colors.textSecondary)
   191→                .multilineTextAlignment(.center)
   192→
   193→            // Quick prompts
   194→            VStack(spacing: AppTheme.Spacing.sm) {
   195→                Text("Pergunte algo como:")
   196→                    .font(AppTheme.Typography.caption)
   197→                    .foregroundColor(AppTheme.Colors.textTertiary)
   198→
   199→                ForEach(quickPrompts, id: \.self) { prompt in
   200→                    Button {
   201→                        userInput = prompt
   202→                        sendMessage()
   203→                    } label: {
   204→                        Text(prompt)
   205→                            .font(AppTheme.Typography.callout)
   206→                            .foregroundColor(.cyan)
   207→                            .padding(.horizontal, AppTheme.Spacing.md)
   208→                            .padding(.vertical, AppTheme.Spacing.sm)
   209→                            .background(
   210→                                Capsule()
   211→                                    .stroke(Color.cyan.opacity(0.5), lineWidth: 1)
   212→                            )
   213→                    }
   214→                    .buttonStyle(.plain)
   215→                }
   216→            }
   217→        }
   218→        .padding(AppTheme.Spacing.xl)
   219→    }
   220→
   221→    private var quickPrompts: [String] {
   222→        [
   223→            "Como posso quitar minhas dividas mais rapido?",
   224→            "Analise minha saude financeira",
   225→            "Qual divida devo priorizar?",
   226→            "Dicas para economizar este mes"
   227→        ]
   228→    }
   229→
   230→    // MARK: - Thinking View
   231→    private var thinkingView: some View {
   232→        HStack(alignment: .top, spacing: AppTheme.Spacing.sm) {
   233→            ZStack {
   234→                Circle()
   235→                    .fill(Color.cyan.opacity(0.15))
   236→                    .frame(width: 32, height: 32)
   237→
   238→                Image(systemName: "brain.head.profile")
   239→                    .font(.caption)
   240→                    .foregroundColor(.cyan)
   241→            }
   242→
   243→            VStack(alignment: .leading, spacing: 4) {
   244→                HStack(spacing: 4) {
   245→                    ForEach(0..<3) { index in
   246→                        Circle()
   247→                            .fill(Color.cyan)
   248→                            .frame(width: 6, height: 6)
   249→                            .opacity(0.5)
   250→                            .animation(
   251→                                Animation.easeInOut(duration: 0.6)
   252→                                    .repeatForever()
   253→                                    .delay(Double(index) * 0.2),
   254→                                value: isThinking
   255→                            )
   256→                    }
   257→                }
   258→                .padding(.horizontal, AppTheme.Spacing.md)
   259→                .padding(.vertical, AppTheme.Spacing.sm)
   260→                .background(AppTheme.Colors.elevatedBackground)
   261→                .cornerRadius(AppTheme.Radius.medium)
   262→            }
   263→
   264→            Spacer()
   265→        }
   266→    }
   267→
   268→    // MARK: - Input View
   269→    private var inputView: some View {
   270→        HStack(spacing: AppTheme.Spacing.md) {
   271→            TextField("Digite sua pergunta...", text: $userInput, axis: .vertical)
   272→                .textFieldStyle(.plain)
   273→                .font(AppTheme.Typography.body)
   274→                .lineLimit(1...5)
   275→                .onSubmit {
   276→                    sendMessage()
   277→                }
   278→
   279→            Button {
   280→                sendMessage()
   281→            } label: {
   282→                ZStack {
   283→                    if userInput.isEmpty {
   284→                        Circle()
   285→                            .fill(Color.gray.opacity(0.3))
   286→                            .frame(width: 36, height: 36)
   287→                    } else {
   288→                        Circle()
   289→                            .fill(
   290→                                LinearGradient(
   291→                                    colors: [.cyan, .purple],
   292→                                    startPoint: .topLeading,
   293→                                    endPoint: .bottomTrailing
   294→                                )
   295→                            )
   296→                            .frame(width: 36, height: 36)
   297→                    }
   298→
   299→                    Image(systemName: "arrow.up")
   300→                        .font(.body.bold())
   301→                        .foregroundColor(userInput.isEmpty ? .gray : .white)
   302→                }
   303→            }
   304→            .buttonStyle(.plain)
   305→            .disabled(userInput.isEmpty || isThinking)
   306→        }
   307→        .padding(AppTheme.Spacing.md)
   308→        .background(AppTheme.Colors.elevatedBackground)
   309→        .cornerRadius(AppTheme.Radius.large)
   310→        .padding(AppTheme.Spacing.lg)
   311→        .background(AppTheme.Colors.cardBackground)
   312→    }
   313→
   314→    // MARK: - Actions
   315→    private func sendMessage() {
   316→        guard !userInput.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty else { return }
   317→
   318→        let userMessage = ChatMessage(role: .user, content: userInput, timestamp: Date())
   319→        messages.append(userMessage)
   320→
   321→        let question = userInput
   322→        userInput = ""
   323→        isThinking = true
   324→
   325→        Task {
   326→            do {
   327→                let response = try await aiService.askAI(question: question, includeContext: true)
   328→                await MainActor.run {
   329→                    messages.append(ChatMessage(role: .assistant, content: response, timestamp: Date()))
   330→                    isThinking = false
   331→                }
   332→            } catch {
   333→                await MainActor.run {
   334→                    messages.append(ChatMessage(
   335→                        role: .system,
   336→                        content: "Erro ao processar sua pergunta: \(error.localizedDescription)",
   337→                        timestamp: Date()
   338→                    ))
   339→                    isThinking = false
   340→                }
   341→            }
   342→        }
   343→    }
   344→}
   345→
   346→// MARK: - Chat Bubble
   347→struct ChatBubble: View {
   348→    let message: AIAssistantView.ChatMessage
   349→
   350→    var isUser: Bool {
   351→        message.role == .user
   352→    }
   353→
   354→    var body: some View {
   355→        HStack(alignment: .top, spacing: AppTheme.Spacing.sm) {
   356→            if isUser {
   357→                Spacer()
   358→            } else {
   359→                // AI Avatar
   360→                ZStack {
   361→                    Circle()
   362→                        .fill(message.role == .system ? Color.orange.opacity(0.15) : Color.cyan.opacity(0.15))
   363→                        .frame(width: 32, height: 32)
   364→
   365→                    Image(systemName: message.role == .system ? "exclamationmark.triangle" : "brain.head.profile")
   366→                        .font(.caption)
   367→                        .foregroundColor(message.role == .system ? .orange : .cyan)
   368→                }
   369→            }
   370→
   371→            VStack(alignment: isUser ? .trailing : .leading, spacing: 4) {
   372→                Text(message.content)
   373→                    .font(AppTheme.Typography.body)
   374→                    .foregroundColor(isUser ? .white : AppTheme.Colors.textPrimary)
   375→                    .textSelection(.enabled)
   376→                    .padding(.horizontal, AppTheme.Spacing.md)
   377→                    .padding(.vertical, AppTheme.Spacing.sm)
   378→                    .background(
   379→                        RoundedRectangle(cornerRadius: AppTheme.Radius.medium)
   380→                            .fill(isUser ?
   381→                                  AnyShapeStyle(LinearGradient(
   382→                                    colors: [.cyan, .purple],
   383→                                    startPoint: .topLeading,
   384→                                    endPoint: .bottomTrailing
   385→                                  )) :
   386→                                  AnyShapeStyle(AppTheme.Colors.elevatedBackground)
   387→                            )
   388→                    )
   389→
   390→                Text(message.timestamp.formatted(date: .omitted, time: .shortened))
   391→                    .font(AppTheme.Typography.caption2)
   392→                    .foregroundColor(AppTheme.Colors.textTertiary)
   393→            }
   394→
   395→            if isUser {
   396→                // User Avatar
   397→                ZStack {
   398→                    Circle()
   399→                        .fill(Color.indigo.opacity(0.15))
   400→                        .frame(width: 32, height: 32)
   401→
   402→                    Image(systemName: "person.fill")
   403→                        .font(.caption)
   404→                        .foregroundColor(.indigo)
   405→                }
   406→            } else {
   407→                Spacer()
   408→            }
   409→        }
   410→    }
   411→}
   412→
   413→// MARK: - AI Suggestion Badge
   414→struct AISuggestionBadge: View {
   415→    let text: String
   416→    var color: Color = .cyan
   417→
   418→    var body: some View {
   419→        HStack(spacing: 4) {
   420→            Image(systemName: "sparkles")
   421→                .font(.caption2)
   422→            Text(text)
   423→                .font(AppTheme.Typography.caption2)
   424→        }
   425→        .foregroundColor(color)
   426→        .padding(.horizontal, 8)
   427→        .padding(.vertical, 4)
   428→        .background(
   429→            Capsule()
   430→                .fill(color.opacity(0.15))
   431→        )
   432→    }
   433→}
   434→
   435→// MARK: - AI Loading Indicator
   436→struct AILoadingIndicator: View {
   437→    @State private var isAnimating = false
   438→
   439→    var body: some View {
   440→        HStack(spacing: 4) {
   441→            ForEach(0..<3) { index in
   442→                Circle()
   443→                    .fill(Color.cyan)
   444→                    .frame(width: 8, height: 8)
   445→                    .scaleEffect(isAnimating ? 1.0 : 0.5)
   446→                    .animation(
   447→                        Animation.easeInOut(duration: 0.6)
   448→                            .repeatForever()
   449→                            .delay(Double(index) * 0.2),
   450→                        value: isAnimating
   451→                    )
   452→            }
   453→        }
   454→        .onAppear {
   455→            isAnimating = true
   456→        }
   457→    }
   458→}
   459→
   460→#Preview {
   461→    AIAssistantView()
   462→        .environmentObject(DataManager.shared)
   463→}
   464→

<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>

import SwiftUI

// MARK: - AI Insight Card
struct AIInsightCard: View {
    let insight: ClaudeAIService.QuickInsight

    @State private var isHovered = false

    var body: some View {
        HStack(spacing: AppTheme.Spacing.md) {
            // Icon
            ZStack {
                Circle()
                    .fill(insight.type.color.opacity(0.15))
                    .frame(width: 40, height: 40)

                Image(systemName: insight.type.icon)
                    .font(.body)
                    .foregroundColor(insight.type.color)
            }

            VStack(alignment: .leading, spacing: 4) {
                Text(insight.title)
                    .font(AppTheme.Typography.callout)
                    .fontWeight(.semibold)
                    .foregroundColor(AppTheme.Colors.textPrimary)

                Text(insight.description)
                    .font(AppTheme.Typography.caption)
                    .foregroundColor(AppTheme.Colors.textSecondary)
                    .lineLimit(2)
            }

            Spacer()

            if insight.action != nil {
                Image(systemName: "chevron.right")
                    .font(.caption)
                    .foregroundColor(AppTheme.Colors.textTertiary)
            }
        }
        .padding(AppTheme.Spacing.md)
        .background(
            RoundedRectangle(cornerRadius: AppTheme.Radius.medium)
                .fill(isHovered ? insight.type.color.opacity(0.08) : AppTheme.Colors.elevatedBackground)
        )
        .overlay(
            RoundedRectangle(cornerRadius: AppTheme.Radius.medium)
                .stroke(isHovered ? insight.type.color.opacity(0.3) : .clear, lineWidth: 1)
        )
        .scaleEffect(isHovered ? 1.01 : 1.0)
        .animation(AppTheme.Animation.quick, value: isHovered)
        .onHover { hovering in
            isHovered = hovering
        }
    }
}

// MARK: - AI Assistant View (Chat Interface)
struct AIAssistantView: View {
    @StateObject private var aiService = ClaudeAIService.shared
    @EnvironmentObject var dataManager: DataManager
    @Environment(\.dismiss) private var dismiss

    @State private var userInput = ""
    @State private var messages: [ChatMessage] = []
    @State private var isThinking = false

    struct ChatMessage: Identifiable {
        let id = UUID()
        let role: MessageRole
        let content: String
        let timestamp: Date

        enum MessageRole {
            case user, assistant, system
        }
    }

    var body: some View {
        VStack(spacing: 0) {
            // Header
            headerView

            Divider()

            // Messages
            ScrollViewReader { proxy in
                ScrollView {
                    LazyVStack(spacing: AppTheme.Spacing.md) {
                        // Welcome message
                        if messages.isEmpty {
                            welcomeView
                        }

                        ForEach(messages) { message in
                            ChatBubble(message: message)
                                .id(message.id)
                        }

                        if isThinking {
                            thinkingView
                        }
                    }
                    .padding(AppTheme.Spacing.lg)
                }
                .onChange(of: messages.count) { _, _ in
                    if let lastMessage = messages.last {
                        withAnimation {
                            proxy.scrollTo(lastMessage.id, anchor: .bottom)
                        }
                    }
                }
            }

            Divider()

            // Input area
            inputView
        }
        .frame(width: 600, height: 700)
        .background(AppTheme.Colors.background)
    }

    // MARK: - Header
    private var headerView: some View {
        HStack {
            HStack(spacing: AppTheme.Spacing.sm) {
                ZStack {
                    Circle()
                        .fill(
                            LinearGradient(
                                colors: [.cyan, .purple],
                                startPoint: .topLeading,
                                endPoint: .bottomTrailing
                            )
                        )
                        .frame(width: 36, height: 36)

                    Image(systemName: "brain.head.profile")
                        .font(.body)
                        .foregroundColor(.white)
                }

                VStack(alignment: .leading, spacing: 2) {
                    Text("Assistente Financeiro IA")
                        .font(AppTheme.Typography.headline)
                        .foregroundColor(AppTheme.Colors.textPrimary)

                    Text("Powered by Claude")
                        .font(AppTheme.Typography.caption2)
                        .foregroundColor(AppTheme.Colors.textTertiary)
                }
            }

            Spacer()

            Button {
                dismiss()
            } label: {
                Image(systemName: "xmark.circle.fill")
                    .font(.title2)
                    .foregroundColor(AppTheme.Colors.textTertiary)
            }
            .buttonStyle(.plain)
        }
        .padding(AppTheme.Spacing.lg)
        .background(AppTheme.Colors.cardBackground)
    }

    // MARK: - Welcome View
    private var welcomeView: some View {
        VStack(spacing: AppTheme.Spacing.lg) {
            Image(systemName: "sparkles")
                .font(.system(size: 50))
                .foregroundStyle(
                    LinearGradient(
                        colors: [.cyan, .purple],
                        startPoint: .topLeading,
                        endPoint: .bottomTrailing
                    )
                )

            Text("Ola! Sou seu assistente financeiro.")
                .font(AppTheme.Typography.title3)
                .foregroundColor(AppTheme.Colors.textPrimary)

            Text("Posso ajudar com analises, dicas e planejamento financeiro baseado nos seus dados.")
                .font(AppTheme.Typography.body)
                .foregroundColor(AppTheme.Colors.textSecondary)
                .multilineTextAlignment(.center)

            // Quick prompts
            VStack(spacing: AppTheme.Spacing.sm) {
                Text("Pergunte algo como:")
                    .font(AppTheme.Typography.caption)
                    .foregroundColor(AppTheme.Colors.textTertiary)

                ForEach(quickPrompts, id: \.self) { prompt in
                    Button {
                        userInput = prompt
                        sendMessage()
                    } label: {
                        Text(prompt)
                            .font(AppTheme.Typography.callout)
                            .foregroundColor(.cyan)
                            .padding(.horizontal, AppTheme.Spacing.md)
                            .padding(.vertical, AppTheme.Spacing.sm)
                            .background(
                                Capsule()
                                    .stroke(Color.cyan.opacity(0.5), lineWidth: 1)
                            )
                    }
                    .buttonStyle(.plain)
                }
            }
        }
        .padding(AppTheme.Spacing.xl)
    }

    private var quickPrompts: [String] {
        [
            "Como posso quitar minhas dividas mais rapido?",
            "Analise minha saude financeira",
            "Qual divida devo priorizar?",
            "Dicas para economizar este mes"
        ]
    }

    // MARK: - Thinking View
    private var thinkingView: some View {
        HStack(alignment: .top, spacing: AppTheme.Spacing.sm) {
            ZStack {
                Circle()
                    .fill(Color.cyan.opacity(0.15))
                    .frame(width: 32, height: 32)

                Image(systemName: "brain.head.profile")
                    .font(.caption)
                    .foregroundColor(.cyan)
            }

            VStack(alignment: .leading, spacing: 4) {
                HStack(spacing: 4) {
                    ForEach(0..<3) { index in
                        Circle()
                            .fill(Color.cyan)
                            .frame(width: 6, height: 6)
                            .opacity(0.5)
                            .animation(
                                Animation.easeInOut(duration: 0.6)
                                    .repeatForever()
                                    .delay(Double(index) * 0.2),
                                value: isThinking
                            )
                    }
                }
                .padding(.horizontal, AppTheme.Spacing.md)
                .padding(.vertical, AppTheme.Spacing.sm)
                .background(AppTheme.Colors.elevatedBackground)
                .cornerRadius(AppTheme.Radius.medium)
            }

            Spacer()
        }
    }

    // MARK: - Input View
    private var inputView: some View {
        HStack(spacing: AppTheme.Spacing.md) {
            TextField("Digite sua pergunta...", text: $userInput, axis: .vertical)
                .textFieldStyle(.plain)
                .font(AppTheme.Typography.body)
                .lineLimit(1...5)
                .onSubmit {
                    sendMessage()
                }

            Button {
                sendMessage()
            } label: {
                ZStack {
                    if userInput.isEmpty {
                        Circle()
                            .fill(Color.gray.opacity(0.3))
                            .frame(width: 36, height: 36)
                    } else {
                        Circle()
                            .fill(
                                LinearGradient(
                                    colors: [.cyan, .purple],
                                    startPoint: .topLeading,
                                    endPoint: .bottomTrailing
                                )
                            )
                            .frame(width: 36, height: 36)
                    }

                    Image(systemName: "arrow.up")
                        .font(.body.bold())
                        .foregroundColor(userInput.isEmpty ? .gray : .white)
                }
            }
            .buttonStyle(.plain)
            .disabled(userInput.isEmpty || isThinking)
        }
        .padding(AppTheme.Spacing.md)
        .background(AppTheme.Colors.elevatedBackground)
        .cornerRadius(AppTheme.Radius.large)
        .padding(AppTheme.Spacing.lg)
        .background(AppTheme.Colors.cardBackground)
    }

    // MARK: - Actions
    private func sendMessage() {
        guard !userInput.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty else { return }

        let userMessage = ChatMessage(role: .user, content: userInput, timestamp: Date())
        messages.append(userMessage)

        let question = userInput
        userInput = ""
        isThinking = true

        Task {
            do {
                let response = try await aiService.askAI(question: question, includeContext: true)
                await MainActor.run {
                    messages.append(ChatMessage(role: .assistant, content: response, timestamp: Date()))
                    isThinking = false
                }
            } catch {
                await MainActor.run {
                    messages.append(ChatMessage(
                        role: .system,
                        content: "Erro ao processar sua pergunta: \(error.localizedDescription)",
                        timestamp: Date()
                    ))
                    isThinking = false
                }
            }
        }
    }
}

// MARK: - Chat Bubble
struct ChatBubble: View {
    let message: AIAssistantView.ChatMessage

    var isUser: Bool {
        message.role == .user
    }

    var body: some View {
        HStack(alignment: .top, spacing: AppTheme.Spacing.sm) {
            if isUser {
                Spacer()
            } else {
                // AI Avatar
                ZStack {
                    Circle()
                        .fill(message.role == .system ? Color.orange.opacity(0.15) : Color.cyan.opacity(0.15))
                        .frame(width: 32, height: 32)

                    Image(systemName: message.role == .system ? "exclamationmark.triangle" : "brain.head.profile")
                        .font(.caption)
                        .foregroundColor(message.role == .system ? .orange : .cyan)
                }
            }

            VStack(alignment: isUser ? .trailing : .leading, spacing: 4) {
                Text(message.content)
                    .font(AppTheme.Typography.body)
                    .foregroundColor(isUser ? .white : AppTheme.Colors.textPrimary)
                    .textSelection(.enabled)
                    .padding(.horizontal, AppTheme.Spacing.md)
                    .padding(.vertical, AppTheme.Spacing.sm)
                    .background(
                        RoundedRectangle(cornerRadius: AppTheme.Radius.medium)
                            .fill(isUser ?
                                  AnyShapeStyle(LinearGradient(
                                    colors: [.cyan, .purple],
                                    startPoint: .topLeading,
                                    endPoint: .bottomTrailing
                                  )) :
                                  AnyShapeStyle(AppTheme.Colors.elevatedBackground)
                            )
                    )

                Text(message.timestamp.formatted(date: .omitted, time: .shortened))
                    .font(AppTheme.Typography.caption2)
                    .foregroundColor(AppTheme.Colors.textTertiary)
            }

            if isUser {
                // User Avatar
                ZStack {
                    Circle()
                        .fill(Color.indigo.opacity(0.15))
                        .frame(width: 32, height: 32)

                    Image(systemName: "person.fill")
                        .font(.caption)
                        .foregroundColor(.indigo)
                }
            } else {
                Spacer()
            }
        }
    }
}

// MARK: - AI Suggestion Badge
struct AISuggestionBadge: View {
    let text: String
    var color: Color = .cyan

    var body: some View {
        HStack(spacing: 4) {
            Image(systemName: "sparkles")
                .font(.caption2)
            Text(text)
                .font(AppTheme.Typography.caption2)
        }
        .foregroundColor(color)
        .padding(.horizontal, 8)
        .padding(.vertical, 4)
        .background(
            Capsule()
                .fill(color.opacity(0.15))
        )
    }
}

// MARK: - AI Loading Indicator
struct AILoadingIndicator: View {
    @State private var isAnimating = false

    var body: some View {
        HStack(spacing: 4) {
            ForEach(0..<3) { index in
                Circle()
                    .fill(Color.cyan)
                    .frame(width: 8, height: 8)
                    .scaleEffect(isAnimating ? 1.0 : 0.5)
                    .animation(
                        Animation.easeInOut(duration: 0.6)
                            .repeatForever()
                            .delay(Double(index) * 0.2),
                        value: isAnimating
                    )
            }
        }
        .onAppear {
            isAnimating = true
        }
    }
}

#Preview {
    AIAssistantView()
        .environmentObject(DataManager.shared)
}


     1→import Foundation
     2→
     3→extension Double {
     4→    /// Formata para Real Brasileiro: R$ 1.234,56
     5→    func toBRL() -> String {
     6→        let formatter = NumberFormatter()
     7→        formatter.numberStyle = .currency
     8→        formatter.locale = Locale(identifier: "pt_BR")
     9→        formatter.currencySymbol = "R$ "
    10→        return formatter.string(from: NSNumber(value: self)) ?? "R$ 0,00"
    11→    }
    12→
    13→    /// Formato compacto: R$ 1,2K ou R$ 1,5M
    14→    func toBRLCompact() -> String {
    15→        let absValue = abs(self)
    16→        let sign = self < 0 ? "-" : ""
    17→
    18→        if absValue >= 1_000_000 {
    19→            return "\(sign)R$ \(String(format: "%.1f", absValue / 1_000_000))M"
    20→        } else if absValue >= 1_000 {
    21→            return "\(sign)R$ \(String(format: "%.1f", absValue / 1_000))K"
    22→        } else {
    23→            return toBRL()
    24→        }
    25→    }
    26→
    27→    /// Formato sem símbolo: 1.234,56
    28→    func toDecimalBR() -> String {
    29→        let formatter = NumberFormatter()
    30→        formatter.numberStyle = .decimal
    31→        formatter.locale = Locale(identifier: "pt_BR")
    32→        formatter.minimumFractionDigits = 2
    33→        formatter.maximumFractionDigits = 2
    34→        return formatter.string(from: NSNumber(value: self)) ?? "0,00"
    35→    }
    36→
    37→    /// Formato percentual: 10,5%
    38→    func toPercentage(decimals: Int = 1) -> String {
    39→        return String(format: "%.\(decimals)f%%", self)
    40→    }
    41→}
    42→
    43→extension Date {
    44→    /// Formato brasileiro: 23/12/2025
    45→    func toBRDate() -> String {
    46→        let formatter = DateFormatter()
    47→        formatter.dateFormat = "dd/MM/yyyy"
    48→        formatter.locale = Locale(identifier: "pt_BR")
    49→        return formatter.string(from: self)
    50→    }
    51→
    52→    /// Formato curto: 23/12
    53→    func toShortBRDate() -> String {
    54→        let formatter = DateFormatter()
    55→        formatter.dateFormat = "dd/MM"
    56→        formatter.locale = Locale(identifier: "pt_BR")
    57→        return formatter.string(from: self)
    58→    }
    59→
    60→    /// Nome do mês: Dezembro
    61→    var monthName: String {
    62→        let formatter = DateFormatter()
    63→        formatter.dateFormat = "MMMM"
    64→        formatter.locale = Locale(identifier: "pt_BR")
    65→        return formatter.string(from: self).capitalized
    66→    }
    67→
    68→    /// Dias até a data (negativo se já passou)
    69→    var daysFromNow: Int {
    70→        Calendar.current.dateComponents([.day], from: Date(), to: self).day ?? 0
    71→    }
    72→
    73→    /// Início do mês
    74→    var startOfMonth: Date {
    75→        Calendar.current.date(from: Calendar.current.dateComponents([.year, .month], from: self)) ?? self
    76→    }
    77→
    78→    /// Fim do mês
    79→    var endOfMonth: Date {
    80→        Calendar.current.date(byAdding: DateComponents(month: 1, day: -1), to: startOfMonth) ?? self
    81→    }
    82→}
    83→
    84→extension Int {
    85→    /// Formato com separador de milhar: 1.234
    86→    func toFormattedBR() -> String {
    87→        let formatter = NumberFormatter()
    88→        formatter.numberStyle = .decimal
    89→        formatter.locale = Locale(identifier: "pt_BR")
    90→        return formatter.string(from: NSNumber(value: self)) ?? "\(self)"
    91→    }
    92→}
    93→

<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>

import Foundation

extension Double {
    /// Formata para Real Brasileiro: R$ 1.234,56
    func toBRL() -> String {
        let formatter = NumberFormatter()
        formatter.numberStyle = .currency
        formatter.locale = Locale(identifier: "pt_BR")
        formatter.currencySymbol = "R$ "
        return formatter.string(from: NSNumber(value: self)) ?? "R$ 0,00"
    }

    /// Formato compacto: R$ 1,2K ou R$ 1,5M
    func toBRLCompact() -> String {
        let absValue = abs(self)
        let sign = self < 0 ? "-" : ""

        if absValue >= 1_000_000 {
            return "\(sign)R$ \(String(format: "%.1f", absValue / 1_000_000))M"
        } else if absValue >= 1_000 {
            return "\(sign)R$ \(String(format: "%.1f", absValue / 1_000))K"
        } else {
            return toBRL()
        }
    }

    /// Formato sem símbolo: 1.234,56
    func toDecimalBR() -> String {
        let formatter = NumberFormatter()
        formatter.numberStyle = .decimal
        formatter.locale = Locale(identifier: "pt_BR")
        formatter.minimumFractionDigits = 2
        formatter.maximumFractionDigits = 2
        return formatter.string(from: NSNumber(value: self)) ?? "0,00"
    }

    /// Formato percentual: 10,5%
    func toPercentage(decimals: Int = 1) -> String {
        return String(format: "%.\(decimals)f%%", self)
    }
}

extension Date {
    /// Formato brasileiro: 23/12/2025
    func toBRDate() -> String {
        let formatter = DateFormatter()
        formatter.dateFormat = "dd/MM/yyyy"
        formatter.locale = Locale(identifier: "pt_BR")
        return formatter.string(from: self)
    }

    /// Formato curto: 23/12
    func toShortBRDate() -> String {
        let formatter = DateFormatter()
        formatter.dateFormat = "dd/MM"
        formatter.locale = Locale(identifier: "pt_BR")
        return formatter.string(from: self)
    }

    /// Nome do mês: Dezembro
    var monthName: String {
        let formatter = DateFormatter()
        formatter.dateFormat = "MMMM"
        formatter.locale = Locale(identifier: "pt_BR")
        return formatter.string(from: self).capitalized
    }

    /// Dias até a data (negativo se já passou)
    var daysFromNow: Int {
        Calendar.current.dateComponents([.day], from: Date(), to: self).day ?? 0
    }

    /// Início do mês
    var startOfMonth: Date {
        Calendar.current.date(from: Calendar.current.dateComponents([.year, .month], from: self)) ?? self
    }

    /// Fim do mês
    var endOfMonth: Date {
        Calendar.current.date(byAdding: DateComponents(month: 1, day: -1), to: startOfMonth) ?? self
    }
}

extension Int {
    /// Formato com separador de milhar: 1.234
    func toFormattedBR() -> String {
        let formatter = NumberFormatter()
        formatter.numberStyle = .decimal
        formatter.locale = Locale(identifier: "pt_BR")
        return formatter.string(from: NSNumber(value: self)) ?? "\(self)"
    }
}


Let me continue examining more files to identify patterns:


find /Users/neog/Apps/macOS/NEOG-Finance/Sources/NEOGFinance -type f -name "*Manager.swift" -o -name "*Service.swift" | head -20


     1→import Foundation
     2→import SwiftUI
     3→
     4→@MainActor
     5→class DataManager: ObservableObject {
     6→    static let shared = DataManager()
     7→
     8→    // MARK: - Published Properties
     9→    @Published var debts: [Debt] = []
    10→    @Published var transactions: [Transaction] = []
    11→    @Published var profile: FinancialProfile = .default
    12→    @Published var expectedIncome: Double = 0 // Santander RECEBIDO em 06/01/2026
    13→    @Published var leads: [Lead] = []
    14→    @Published var incomeTasks: [IncomeTask] = []
    15→    @Published var incomeGoal: IncomeGoal = .default
    16→
    17→    // MARK: - UserDefaults Keys
    18→    private let debtsKey = "neog_finance_debts"
    19→    private let transactionsKey = "neog_finance_transactions"
    20→    private let profileKey = "neog_finance_profile"
    21→    private let leadsKey = "neog_finance_leads"
    22→    private let incomeTasksKey = "neog_finance_income_tasks"
    23→    private let incomeGoalKey = "neog_finance_income_goal"
    24→
    25→    // MARK: - Init
    26→    private init() {
    27→        loadData()
    28→        if debts.isEmpty {
    29→            loadInitialData()
    30→        }
    31→    }
    32→
    33→    // MARK: - Computed Properties
    34→
    35→    var totalDebts: Double {
    36→        debts.filter { $0.status != .paid }.reduce(0) { $0 + $1.amount }
    37→    }
    38→
    39→    var totalRecurringDebts: Double {
    40→        debts.filter { $0.isRecurring && $0.status != .paid }
    41→            .reduce(0) { $0 + ($1.recurringAmount ?? 0) }
    42→    }
    43→
    44→    var overdueDebts: [Debt] {
    45→        debts.filter { $0.isOverdue }.sorted { $0.urgencyLevel > $1.urgencyLevel }
    46→    }
    47→
    48→    var pendingDebts: [Debt] {
    49→        debts.filter { $0.status == .pending && !$0.isOverdue }
    50→            .sorted { ($0.dueDate ?? .distantFuture) < ($1.dueDate ?? .distantFuture) }
    51→    }
    52→
    53→    var paidDebts: [Debt] {
    54→        debts.filter { $0.status == .paid }
    55→    }
    56→
    57→    var sortedDebtsByUrgency: [Debt] {
    58→        debts.filter { $0.status != .paid }.sorted { $0.urgencyLevel > $1.urgencyLevel }
    59→    }
    60→
    61→    var monthlyIncome: Double {
    62→        currentMonthTransactions
    63→            .filter { $0.type == .income }
    64→            .reduce(0) { $0 + $1.amount }
    65→    }
    66→
    67→    var monthlyExpenses: Double {
    68→        currentMonthTransactions
    69→            .filter { $0.type == .expense }
    70→            .reduce(0) { $0 + $1.amount }
    71→    }
    72→
    73→    var monthlyBalance: Double {
    74→        monthlyIncome - monthlyExpenses
    75→    }
    76→
    77→    var currentMonthTransactions: [Transaction] {
    78→        let calendar = Calendar.current
    79→        return transactions.filter {
    80→            calendar.isDate($0.date, equalTo: Date(), toGranularity: .month)
    81→        }
    82→    }
    83→
    84→    var recurringExpenses: [Transaction] {
    85→        transactions.filter { $0.type == .expense && $0.isRecurring }
    86→    }
    87→
    88→    var totalRecurringExpenses: Double {
    89→        recurringExpenses.reduce(0) { $0 + $1.amount }
    90→    }
    91→
    92→    // MARK: - Renda Total (Centralizado - evita +1000 hardcoded)
    93→
    94→    /// Valor do LOAS da irmã (benefício fixo mensal)
    95→    var loasIncome: Double { 1000 }
    96→
    97→    /// Renda mensal total = Freelance + LOAS
    98→    var totalMonthlyIncome: Double {
    99→        profile.monthlyIncomeAverage + loasIncome
   100→    }
   101→
   102→    var projectedMonthlyBalance: Double {
   103→        totalMonthlyIncome - totalRecurringExpenses - totalRecurringDebts
   104→    }
   105→
   106→    var balanceAfterLoan: Double {
   107→        expectedIncome - totalDebts
   108→    }
   109→
   110→    // MARK: - Leads Computed Properties
   111→
   112→    var totalLeadsValue: Double {
   113→        leads.reduce(0) { $0 + $1.value }
   114→    }
   115→
   116→    var closedLeadsValue: Double {
   117→        leads.filter { $0.status == .closed }.reduce(0) { $0 + $1.value }
   118→    }
   119→
   120→    var pendingLeadsValue: Double {
   121→        leads.filter { $0.status == .proposal || $0.status == .negotiating }.reduce(0) { $0 + $1.value }
   122→    }
   123→
   124→    var incomeGoalProgress: Double {
   125→        guard incomeGoal.targetAmount > 0 else { return 0 }
   126→        return min(closedLeadsValue / incomeGoal.targetAmount, 1.0)
   127→    }
   128→
   129→    var daysUntilGoalDeadline: Int {
   130→        Calendar.current.dateComponents([.day], from: Date(), to: incomeGoal.deadline).day ?? 0
   131→    }
   132→
   133→    // MARK: - CRUD Operations
   134→
   135→    func addDebt(_ debt: Debt) {
   136→        debts.append(debt)
   137→        saveData()
   138→    }
   139→
   140→    func updateDebt(_ debt: Debt) {
   141→        if let index = debts.firstIndex(where: { $0.id == debt.id }) {
   142→            debts[index] = debt
   143→            saveData()
   144→        }
   145→    }
   146→
   147→    func deleteDebt(_ debt: Debt) {
   148→        debts.removeAll { $0.id == debt.id }
   149→        saveData()
   150→    }

<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>

import Foundation
import SwiftUI

@MainActor
class DataManager: ObservableObject {
    static let shared = DataManager()

    // MARK: - Published Properties
    @Published var debts: [Debt] = []
    @Published var transactions: [Transaction] = []
    @Published var profile: FinancialProfile = .default
    @Published var expectedIncome: Double = 0 // Santander RECEBIDO em 06/01/2026
    @Published var leads: [Lead] = []
    @Published var incomeTasks: [IncomeTask] = []
    @Published var incomeGoal: IncomeGoal = .default

    // MARK: - UserDefaults Keys
    private let debtsKey = "neog_finance_debts"
    private let transactionsKey = "neog_finance_transactions"
    private let profileKey = "neog_finance_profile"
    private let leadsKey = "neog_finance_leads"
    private let incomeTasksKey = "neog_finance_income_tasks"
    private let incomeGoalKey = "neog_finance_income_goal"

    // MARK: - Init
    private init() {
        loadData()
        if debts.isEmpty {
            loadInitialData()
        }
    }

    // MARK: - Computed Properties

    var totalDebts: Double {
        debts.filter { $0.status != .paid }.reduce(0) { $0 + $1.amount }
    }

    var totalRecurringDebts: Double {
        debts.filter { $0.isRecurring && $0.status != .paid }
            .reduce(0) { $0 + ($1.recurringAmount ?? 0) }
    }

    var overdueDebts: [Debt] {
        debts.filter { $0.isOverdue }.sorted { $0.urgencyLevel > $1.urgencyLevel }
    }

    var pendingDebts: [Debt] {
        debts.filter { $0.status == .pending && !$0.isOverdue }
            .sorted { ($0.dueDate ?? .distantFuture) < ($1.dueDate ?? .distantFuture) }
    }

    var paidDebts: [Debt] {
        debts.filter { $0.status == .paid }
    }

    var sortedDebtsByUrgency: [Debt] {
        debts.filter { $0.status != .paid }.sorted { $0.urgencyLevel > $1.urgencyLevel }
    }

    var monthlyIncome: Double {
        currentMonthTransactions
            .filter { $0.type == .income }
            .reduce(0) { $0 + $1.amount }
    }

    var monthlyExpenses: Double {
        currentMonthTransactions
            .filter { $0.type == .expense }
            .reduce(0) { $0 + $1.amount }
    }

    var monthlyBalance: Double {
        monthlyIncome - monthlyExpenses
    }

    var currentMonthTransactions: [Transaction] {
        let calendar = Calendar.current
        return transactions.filter {
            calendar.isDate($0.date, equalTo: Date(), toGranularity: .month)
        }
    }

    var recurringExpenses: [Transaction] {
        transactions.filter { $0.type == .expense && $0.isRecurring }
    }

    var totalRecurringExpenses: Double {
        recurringExpenses.reduce(0) { $0 + $1.amount }
    }

    // MARK: - Renda Total (Centralizado - evita +1000 hardcoded)

    /// Valor do LOAS da irmã (benefício fixo mensal)
    var loasIncome: Double { 1000 }

    /// Renda mensal total = Freelance + LOAS
    var totalMonthlyIncome: Double {
        profile.monthlyIncomeAverage + loasIncome
    }

    var projectedMonthlyBalance: Double {
        totalMonthlyIncome - totalRecurringExpenses - totalRecurringDebts
    }

    var balanceAfterLoan: Double {
        expectedIncome - totalDebts
    }

    // MARK: - Leads Computed Properties

    var totalLeadsValue: Double {
        leads.reduce(0) { $0 + $1.value }
    }

    var closedLeadsValue: Double {
        leads.filter { $0.status == .closed }.reduce(0) { $0 + $1.value }
    }

    var pendingLeadsValue: Double {
        leads.filter { $0.status == .proposal || $0.status == .negotiating }.reduce(0) { $0 + $1.value }
    }

    var incomeGoalProgress: Double {
        guard incomeGoal.targetAmount > 0 else { return 0 }
        return min(closedLeadsValue / incomeGoal.targetAmount, 1.0)
    }

    var daysUntilGoalDeadline: Int {
        Calendar.current.dateComponents([.day], from: Date(), to: incomeGoal.deadline).day ?? 0
    }

    // MARK: - CRUD Operations

    func addDebt(_ debt: Debt) {
        debts.append(debt)
        saveData()
    }

    func updateDebt(_ debt: Debt) {
        if let index = debts.firstIndex(where: { $0.id == debt.id }) {
            debts[index] = debt
            saveData()
        }
    }

    func deleteDebt(_ debt: Debt) {
        debts.removeAll { $0.id == debt.id }
        saveData()
    }