const Date = @import("../Date.zig");

pub const ReportTime = enum {
    bmo, // before market open
    amc, // after market close
    dmh, // during market hours
    unknown,
};

/// An earnings event (historical or upcoming).
pub const EarningsEvent = struct {
    symbol: []const u8 = "",
    date: Date,
    /// Estimated EPS (analyst consensus)
    estimate: ?f64 = null,
    /// Actual reported EPS (null if upcoming)
    actual: ?f64 = null,
    /// Surprise amount (actual - estimate)
    surprise: ?f64 = null,
    /// Surprise percentage
    surprise_percent: ?f64 = null,
    /// Fiscal quarter (1-4)
    quarter: ?u8 = null,
    /// Fiscal year
    fiscal_year: ?i16 = null,
    /// Revenue actual
    revenue_actual: ?f64 = null,
    /// Revenue estimate
    revenue_estimate: ?f64 = null,
    /// When earnings are reported relative to market hours
    report_time: ReportTime = .unknown,

    pub fn isFuture(self: EarningsEvent) bool {
        return self.actual == null;
    }

    pub fn surpriseAmount(self: EarningsEvent) ?f64 {
        if (self.surprise) |s| return s;
        const act = self.actual orelse return null;
        const est = self.estimate orelse return null;
        return act - est;
    }

    pub fn surprisePct(self: EarningsEvent) ?f64 {
        if (self.surprise_percent) |s| return s;
        const act = self.actual orelse return null;
        const est = self.estimate orelse return null;
        if (est == 0) return null;
        return ((act - est) / @abs(est)) * 100.0;
    }
};

const std = @import("std");

test "isFuture" {
    const future = EarningsEvent{ .symbol = "AAPL", .date = Date{ .days = 20000 }, .estimate = 1.5 };
    try std.testing.expect(future.isFuture());
    const past = EarningsEvent{ .symbol = "AAPL", .date = Date{ .days = 19000 }, .actual = 1.6, .estimate = 1.5 };
    try std.testing.expect(!past.isFuture());
}

test "surpriseAmount" {
    // With surprise field set directly
    const with_surprise = EarningsEvent{ .symbol = "AAPL", .date = Date{ .days = 19000 }, .surprise = 0.15 };
    try std.testing.expectApproxEqAbs(@as(f64, 0.15), with_surprise.surpriseAmount().?, 0.001);
    // With actual and estimate (computed)
    const computed = EarningsEvent{ .symbol = "AAPL", .date = Date{ .days = 19000 }, .actual = 1.65, .estimate = 1.50 };
    try std.testing.expectApproxEqAbs(@as(f64, 0.15), computed.surpriseAmount().?, 0.001);
    // Missing actual -> null
    const no_actual = EarningsEvent{ .symbol = "AAPL", .date = Date{ .days = 19000 }, .estimate = 1.50 };
    try std.testing.expect(no_actual.surpriseAmount() == null);
    // Missing estimate -> null
    const no_estimate = EarningsEvent{ .symbol = "AAPL", .date = Date{ .days = 19000 }, .actual = 1.65 };
    try std.testing.expect(no_estimate.surpriseAmount() == null);
}

test "surprisePct" {
    // With surprise_percent set directly
    const with_pct = EarningsEvent{ .symbol = "AAPL", .date = Date{ .days = 19000 }, .surprise_percent = 10.0 };
    try std.testing.expectApproxEqAbs(@as(f64, 10.0), with_pct.surprisePct().?, 0.001);
    // Computed: actual=1.65, estimate=1.50 -> (0.15/1.50)*100 = 10%
    const computed = EarningsEvent{ .symbol = "AAPL", .date = Date{ .days = 19000 }, .actual = 1.65, .estimate = 1.50 };
    try std.testing.expectApproxEqAbs(@as(f64, 10.0), computed.surprisePct().?, 0.001);
    // Estimate = 0 -> null (division by zero guard)
    const zero_est = EarningsEvent{ .symbol = "AAPL", .date = Date{ .days = 19000 }, .actual = 1.0, .estimate = 0.0 };
    try std.testing.expect(zero_est.surprisePct() == null);
    // Negative surprise: actual < estimate
    const miss = EarningsEvent{ .symbol = "AAPL", .date = Date{ .days = 19000 }, .actual = 1.35, .estimate = 1.50 };
    try std.testing.expect(miss.surprisePct().? < 0);
}
