import CodexBarCore
import Testing

struct CodexDashboardAuthorityTests {
    @Test
    func `email only wrong email returns fail closed`() {
        let input = CodexDashboardAuthorityInput(
            sourceKind: .liveWeb,
            proof: CodexDashboardOwnershipProofContext(
                currentIdentity: .emailOnly(normalizedEmail: "owner@example.com"),
                expectedScopedEmail: nil,
                trustedCurrentUsageEmail: nil,
                dashboardSignedInEmail: "other@example.com",
                knownOwners: [
                    CodexDashboardKnownOwnerCandidate(
                        identity: .emailOnly(normalizedEmail: "owner@example.com"),
                        normalizedEmail: "owner@example.com"),
                ]),
            routing: CodexDashboardRoutingHints(
                targetEmail: "owner@example.com",
                lastKnownDashboardRoutingEmail: nil))

        let decision = CodexDashboardAuthority.evaluate(input)

        #expect(decision.disposition == .failClosed)
        #expect(decision.reason == .wrongEmail(expected: "owner@example.com", actual: "other@example.com"))
    }

    @Test
    func `provider account wrong email returns fail closed`() {
        let input = CodexDashboardAuthorityInput(
            sourceKind: .liveWeb,
            proof: CodexDashboardOwnershipProofContext(
                currentIdentity: .providerAccount(id: "acct-owner"),
                expectedScopedEmail: "owner@example.com",
                trustedCurrentUsageEmail: nil,
                dashboardSignedInEmail: "other@example.com",
                knownOwners: [
                    CodexDashboardKnownOwnerCandidate(
                        identity: .providerAccount(id: "acct-owner"),
                        normalizedEmail: "owner@example.com"),
                ]),
            routing: CodexDashboardRoutingHints(
                targetEmail: "owner@example.com",
                lastKnownDashboardRoutingEmail: "stale@example.com"))

        let decision = CodexDashboardAuthority.evaluate(input)

        #expect(decision.disposition == .failClosed)
        #expect(decision.reason == .wrongEmail(expected: "owner@example.com", actual: "other@example.com"))
    }

    @Test
    func `email only same email ambiguity returns display only`() {
        let input = CodexDashboardAuthorityInput(
            sourceKind: .liveWeb,
            proof: CodexDashboardOwnershipProofContext(
                currentIdentity: .emailOnly(normalizedEmail: "shared@example.com"),
                expectedScopedEmail: nil,
                trustedCurrentUsageEmail: nil,
                dashboardSignedInEmail: "shared@example.com",
                knownOwners: [
                    CodexDashboardKnownOwnerCandidate(
                        identity: .providerAccount(id: "acct-alpha"),
                        normalizedEmail: "shared@example.com"),
                    CodexDashboardKnownOwnerCandidate(
                        identity: .providerAccount(id: "acct-beta"),
                        normalizedEmail: "shared@example.com"),
                ]),
            routing: CodexDashboardRoutingHints(
                targetEmail: "shared@example.com",
                lastKnownDashboardRoutingEmail: "shared@example.com"))

        let decision = CodexDashboardAuthority.evaluate(input)

        #expect(decision.disposition == .displayOnly)
        #expect(decision.reason == .sameEmailAmbiguity(email: "shared@example.com"))
    }

    @Test
    func `provider account exact owner match returns attach`() {
        let input = CodexDashboardAuthorityInput(
            sourceKind: .liveWeb,
            proof: CodexDashboardOwnershipProofContext(
                currentIdentity: .providerAccount(id: "acct-owner"),
                expectedScopedEmail: "OWNER@example.com",
                trustedCurrentUsageEmail: nil,
                dashboardSignedInEmail: "owner@example.com",
                knownOwners: [
                    CodexDashboardKnownOwnerCandidate(
                        identity: .providerAccount(id: "acct-owner"),
                        normalizedEmail: "OWNER@example.com"),
                    CodexDashboardKnownOwnerCandidate(
                        identity: .providerAccount(id: "acct-other"),
                        normalizedEmail: "other@example.com"),
                ]),
            routing: CodexDashboardRoutingHints(
                targetEmail: "route@example.com",
                lastKnownDashboardRoutingEmail: "stale@example.com"))

        let decision = CodexDashboardAuthority.evaluate(input)

        #expect(decision.disposition == .attach)
        #expect(decision.reason == .exactProviderAccountMatch)
    }

    @Test
    func `provider account exact owner ignores duplicate profile isolation`() {
        let input = CodexDashboardAuthorityInput(
            sourceKind: .liveWeb,
            proof: CodexDashboardOwnershipProofContext(
                currentIdentity: .providerAccount(id: "acct-owner"),
                expectedScopedEmail: "owner@example.com",
                trustedCurrentUsageEmail: nil,
                dashboardSignedInEmail: "owner@example.com",
                knownOwners: [
                    CodexDashboardKnownOwnerCandidate(
                        identity: .providerAccount(id: "acct-owner"),
                        normalizedEmail: "owner@example.com",
                        sourceIsolationIdentifier: "profile-a"),
                    CodexDashboardKnownOwnerCandidate(
                        identity: .providerAccount(id: "acct-owner"),
                        normalizedEmail: "owner@example.com",
                        sourceIsolationIdentifier: "profile-b"),
                ]),
            routing: CodexDashboardRoutingHints(
                targetEmail: "owner@example.com",
                lastKnownDashboardRoutingEmail: nil))

        let decision = CodexDashboardAuthority.evaluate(input)

        #expect(decision.disposition == .attach)
        #expect(decision.reason == .exactProviderAccountMatch)
    }

    @Test
    func `email only owners retain profile isolation ambiguity`() {
        let input = CodexDashboardAuthorityInput(
            sourceKind: .liveWeb,
            proof: CodexDashboardOwnershipProofContext(
                currentIdentity: .emailOnly(normalizedEmail: "shared@example.com"),
                expectedScopedEmail: "shared@example.com",
                trustedCurrentUsageEmail: nil,
                dashboardSignedInEmail: "shared@example.com",
                knownOwners: [
                    CodexDashboardKnownOwnerCandidate(
                        identity: .emailOnly(normalizedEmail: "shared@example.com"),
                        normalizedEmail: "shared@example.com",
                        sourceIsolationIdentifier: "profile-a"),
                    CodexDashboardKnownOwnerCandidate(
                        identity: .emailOnly(normalizedEmail: "shared@example.com"),
                        normalizedEmail: "shared@example.com",
                        sourceIsolationIdentifier: "profile-b"),
                ]),
            routing: CodexDashboardRoutingHints(
                targetEmail: "shared@example.com",
                lastKnownDashboardRoutingEmail: nil))

        let decision = CodexDashboardAuthority.evaluate(input)

        #expect(decision.disposition == .displayOnly)
        #expect(decision.reason == .sameEmailAmbiguity(email: "shared@example.com"))
    }

    @Test
    func `provider account exact owner stays display only when email has another owner`() {
        let input = CodexDashboardAuthorityInput(
            sourceKind: .liveWeb,
            proof: CodexDashboardOwnershipProofContext(
                currentIdentity: .providerAccount(id: "acct-current"),
                expectedScopedEmail: "shared@example.com",
                trustedCurrentUsageEmail: nil,
                dashboardSignedInEmail: "shared@example.com",
                knownOwners: [
                    CodexDashboardKnownOwnerCandidate(
                        identity: .providerAccount(id: "acct-current"),
                        normalizedEmail: "shared@example.com"),
                    CodexDashboardKnownOwnerCandidate(
                        identity: .providerAccount(id: "acct-other"),
                        normalizedEmail: "shared@example.com"),
                ]),
            routing: CodexDashboardRoutingHints(
                targetEmail: "shared@example.com",
                lastKnownDashboardRoutingEmail: nil))

        let decision = CodexDashboardAuthority.evaluate(input)

        #expect(decision.disposition == .displayOnly)
        #expect(decision.reason == .sameEmailAmbiguity(email: "shared@example.com"))
    }

    @Test
    func `provider account same email ambiguity without exact match returns display only`() {
        let input = CodexDashboardAuthorityInput(
            sourceKind: .liveWeb,
            proof: CodexDashboardOwnershipProofContext(
                currentIdentity: .providerAccount(id: "acct-current"),
                expectedScopedEmail: "shared@example.com",
                trustedCurrentUsageEmail: nil,
                dashboardSignedInEmail: "shared@example.com",
                knownOwners: [
                    CodexDashboardKnownOwnerCandidate(
                        identity: .providerAccount(id: "acct-alpha"),
                        normalizedEmail: "shared@example.com"),
                    CodexDashboardKnownOwnerCandidate(
                        identity: .providerAccount(id: "acct-beta"),
                        normalizedEmail: "shared@example.com"),
                ]),
            routing: CodexDashboardRoutingHints(
                targetEmail: "shared@example.com",
                lastKnownDashboardRoutingEmail: nil))

        let decision = CodexDashboardAuthority.evaluate(input)

        #expect(decision.disposition == .displayOnly)
        #expect(decision.reason == .sameEmailAmbiguity(email: "shared@example.com"))
    }

    @Test
    func `provider account nil scoped email with dashboard collision returns fail closed`() {
        let input = CodexDashboardAuthorityInput(
            sourceKind: .liveWeb,
            proof: CodexDashboardOwnershipProofContext(
                currentIdentity: .providerAccount(id: "acct-current"),
                expectedScopedEmail: nil,
                trustedCurrentUsageEmail: "shared@example.com",
                dashboardSignedInEmail: "shared@example.com",
                knownOwners: [
                    CodexDashboardKnownOwnerCandidate(
                        identity: .providerAccount(id: "acct-alpha"),
                        normalizedEmail: "shared@example.com"),
                    CodexDashboardKnownOwnerCandidate(
                        identity: .providerAccount(id: "acct-beta"),
                        normalizedEmail: "shared@example.com"),
                ]),
            routing: CodexDashboardRoutingHints(
                targetEmail: "shared@example.com",
                lastKnownDashboardRoutingEmail: nil))

        let decision = CodexDashboardAuthority.evaluate(input)

        #expect(decision.disposition == .failClosed)
        #expect(decision.reason == .providerAccountMissingScopedEmail)
    }

    @Test
    func `unresolved trusted continuity without competing owner returns attach`() {
        let input = CodexDashboardAuthorityInput(
            sourceKind: .liveWeb,
            proof: CodexDashboardOwnershipProofContext(
                currentIdentity: .unresolved,
                expectedScopedEmail: nil,
                trustedCurrentUsageEmail: "owner@example.com",
                dashboardSignedInEmail: "owner@example.com",
                knownOwners: [
                    CodexDashboardKnownOwnerCandidate(
                        identity: .providerAccount(id: "acct-owner"),
                        normalizedEmail: "owner@example.com"),
                ]),
            routing: CodexDashboardRoutingHints(
                targetEmail: "owner@example.com",
                lastKnownDashboardRoutingEmail: "route@example.com"))

        let decision = CodexDashboardAuthority.evaluate(input)

        #expect(decision.disposition == .attach)
        #expect(decision.reason == .trustedContinuityNoCompetingOwner)
    }

    @Test
    func `unresolved trusted continuity with competing owner returns display only`() {
        let input = CodexDashboardAuthorityInput(
            sourceKind: .liveWeb,
            proof: CodexDashboardOwnershipProofContext(
                currentIdentity: .unresolved,
                expectedScopedEmail: nil,
                trustedCurrentUsageEmail: "shared@example.com",
                dashboardSignedInEmail: "shared@example.com",
                knownOwners: [
                    CodexDashboardKnownOwnerCandidate(
                        identity: .providerAccount(id: "acct-alpha"),
                        normalizedEmail: "shared@example.com"),
                    CodexDashboardKnownOwnerCandidate(
                        identity: .providerAccount(id: "acct-beta"),
                        normalizedEmail: "shared@example.com"),
                ]),
            routing: CodexDashboardRoutingHints(
                targetEmail: "shared@example.com",
                lastKnownDashboardRoutingEmail: "route@example.com"))

        let decision = CodexDashboardAuthority.evaluate(input)

        #expect(decision.disposition == .displayOnly)
        #expect(decision.reason == .sameEmailAmbiguity(email: "shared@example.com"))
    }

    @Test
    func `unresolved without trusted evidence returns fail closed`() {
        let input = CodexDashboardAuthorityInput(
            sourceKind: .liveWeb,
            proof: CodexDashboardOwnershipProofContext(
                currentIdentity: .unresolved,
                expectedScopedEmail: nil,
                trustedCurrentUsageEmail: nil,
                dashboardSignedInEmail: "owner@example.com",
                knownOwners: []),
            routing: CodexDashboardRoutingHints(
                targetEmail: "owner@example.com",
                lastKnownDashboardRoutingEmail: nil))

        let decision = CodexDashboardAuthority.evaluate(input)

        #expect(decision.disposition == .failClosed)
        #expect(decision.reason == .unresolvedWithoutTrustedEvidence)
    }

    @Test
    func `missing dashboard signed in email returns fail closed`() {
        let input = CodexDashboardAuthorityInput(
            sourceKind: .liveWeb,
            proof: CodexDashboardOwnershipProofContext(
                currentIdentity: .providerAccount(id: "acct-owner"),
                expectedScopedEmail: "owner@example.com",
                trustedCurrentUsageEmail: "owner@example.com",
                dashboardSignedInEmail: nil,
                knownOwners: [
                    CodexDashboardKnownOwnerCandidate(
                        identity: .providerAccount(id: "acct-owner"),
                        normalizedEmail: "owner@example.com"),
                ]),
            routing: CodexDashboardRoutingHints(
                targetEmail: "owner@example.com",
                lastKnownDashboardRoutingEmail: nil))

        let decision = CodexDashboardAuthority.evaluate(input)

        #expect(decision.disposition == .failClosed)
        #expect(decision.reason == .missingDashboardSignedInEmail)
    }

    @Test
    func `live web attach exposes usage credits guard and history effects`() {
        let input = CodexDashboardAuthorityInput(
            sourceKind: .liveWeb,
            proof: CodexDashboardOwnershipProofContext(
                currentIdentity: .providerAccount(id: "acct-owner"),
                expectedScopedEmail: "owner@example.com",
                trustedCurrentUsageEmail: nil,
                dashboardSignedInEmail: "owner@example.com",
                knownOwners: [
                    CodexDashboardKnownOwnerCandidate(
                        identity: .providerAccount(id: "acct-owner"),
                        normalizedEmail: "owner@example.com"),
                ]),
            routing: CodexDashboardRoutingHints(
                targetEmail: nil,
                lastKnownDashboardRoutingEmail: nil))

        let decision = CodexDashboardAuthority.evaluate(input)

        #expect(decision.disposition == .attach)
        #expect(decision.allowedEffects == Set([
            .usageBackfill,
            .creditsAttachment,
            .refreshGuardSeed,
            .historicalBackfill,
        ]))
        #expect(decision.cleanup.isEmpty)
    }

    @Test
    func `cached dashboard attach exposes cached reuse only`() {
        let input = CodexDashboardAuthorityInput(
            sourceKind: .cachedDashboard,
            proof: CodexDashboardOwnershipProofContext(
                currentIdentity: .emailOnly(normalizedEmail: "owner@example.com"),
                expectedScopedEmail: nil,
                trustedCurrentUsageEmail: nil,
                dashboardSignedInEmail: "owner@example.com",
                knownOwners: [
                    CodexDashboardKnownOwnerCandidate(
                        identity: .providerAccount(id: "acct-owner"),
                        normalizedEmail: "owner@example.com"),
                ]),
            routing: CodexDashboardRoutingHints(
                targetEmail: "owner@example.com",
                lastKnownDashboardRoutingEmail: nil))

        let decision = CodexDashboardAuthority.evaluate(input)

        #expect(decision.disposition == .attach)
        #expect(decision.allowedEffects == Set([.cachedDashboardReuse]))
        #expect(decision.cleanup.isEmpty)
    }

    @Test
    func `display only emits full cleanup set`() {
        let input = CodexDashboardAuthorityInput(
            sourceKind: .liveWeb,
            proof: CodexDashboardOwnershipProofContext(
                currentIdentity: .emailOnly(normalizedEmail: "shared@example.com"),
                expectedScopedEmail: nil,
                trustedCurrentUsageEmail: nil,
                dashboardSignedInEmail: "shared@example.com",
                knownOwners: [
                    CodexDashboardKnownOwnerCandidate(
                        identity: .providerAccount(id: "acct-alpha"),
                        normalizedEmail: "shared@example.com"),
                    CodexDashboardKnownOwnerCandidate(
                        identity: .providerAccount(id: "acct-beta"),
                        normalizedEmail: "shared@example.com"),
                ]),
            routing: CodexDashboardRoutingHints(
                targetEmail: nil,
                lastKnownDashboardRoutingEmail: nil))

        let decision = CodexDashboardAuthority.evaluate(input)

        #expect(decision.disposition == .displayOnly)
        #expect(decision.allowedEffects.isEmpty)
        #expect(decision.cleanup == Set(CodexDashboardCleanup.allCases))
    }

    @Test
    func `fail closed emits full cleanup set`() {
        let input = CodexDashboardAuthorityInput(
            sourceKind: .liveWeb,
            proof: CodexDashboardOwnershipProofContext(
                currentIdentity: .unresolved,
                expectedScopedEmail: nil,
                trustedCurrentUsageEmail: nil,
                dashboardSignedInEmail: "owner@example.com",
                knownOwners: []),
            routing: CodexDashboardRoutingHints(
                targetEmail: nil,
                lastKnownDashboardRoutingEmail: nil))

        let decision = CodexDashboardAuthority.evaluate(input)

        #expect(decision.disposition == .failClosed)
        #expect(decision.allowedEffects.isEmpty)
        #expect(decision.cleanup == Set(CodexDashboardCleanup.allCases))
    }

    @Test
    func `routing hints do not change evaluation result`() {
        let proof = CodexDashboardOwnershipProofContext(
            currentIdentity: .providerAccount(id: "acct-owner"),
            expectedScopedEmail: "owner@example.com",
            trustedCurrentUsageEmail: nil,
            dashboardSignedInEmail: "owner@example.com",
            knownOwners: [
                CodexDashboardKnownOwnerCandidate(
                    identity: .providerAccount(id: "acct-owner"),
                    normalizedEmail: "owner@example.com"),
            ])
        let baseInput = CodexDashboardAuthorityInput(
            sourceKind: .liveWeb,
            proof: proof,
            routing: CodexDashboardRoutingHints(
                targetEmail: "owner@example.com",
                lastKnownDashboardRoutingEmail: "owner@example.com"))
        let conflictingRoutingInput = CodexDashboardAuthorityInput(
            sourceKind: .liveWeb,
            proof: proof,
            routing: CodexDashboardRoutingHints(
                targetEmail: "wrong@example.com",
                lastKnownDashboardRoutingEmail: "stale@example.com"))

        let baseDecision = CodexDashboardAuthority.evaluate(baseInput)
        let conflictingDecision = CodexDashboardAuthority.evaluate(conflictingRoutingInput)

        #expect(baseDecision == conflictingDecision)
    }
}
