import { describe, expect, it } from 'vitest'
import {
  KeyResultSchema,
  ObjectiveSchema,
  GoalsDomainSchema,
  DEFAULT_ORGANIZATION_MODEL_GOALS
} from '../../domains/goals'
import { resolveOrganizationModel } from '../../resolve'

// ---------------------------------------------------------------------------
// Group 1: KeyResultSchema — positive parse
// ---------------------------------------------------------------------------

describe('KeyResultSchema — positive parse', () => {
  it('accepts a fully-populated measurable outcome', () => {
    const result = KeyResultSchema.safeParse({
      id: 'kr-revenue-q1',
      description: 'Increase monthly recurring revenue',
      targetMetric: 'monthly revenue',
      currentValue: 12000,
      targetValue: 20000
    })
    expect(result.success).toBe(true)
    if (result.success) {
      expect(result.data.id).toBe('kr-revenue-q1')
      expect(result.data.currentValue).toBe(12000)
      expect(result.data.targetValue).toBe(20000)
    }
  })

  it('accepts a minimal measurable outcome — targetValue optional', () => {
    const result = KeyResultSchema.safeParse({
      id: 'kr-nps',
      description: 'Improve net promoter score',
      targetMetric: 'NPS score'
    })
    expect(result.success).toBe(true)
    if (result.success) {
      expect(result.data.currentValue).toBe(0)
      expect(result.data.targetValue).toBeUndefined()
    }
  })

  it('currentValue defaults to 0 when omitted', () => {
    const result = KeyResultSchema.safeParse({
      id: 'kr-default',
      description: 'Reduce churn',
      targetMetric: 'monthly churn rate'
    })
    expect(result.success).toBe(true)
    if (result.success) {
      expect(result.data.currentValue).toBe(0)
    }
  })
})

// ---------------------------------------------------------------------------
// Group 2: KeyResultSchema — negative parse
// ---------------------------------------------------------------------------

describe('KeyResultSchema — negative parse', () => {
  it('rejects missing id', () => {
    const result = KeyResultSchema.safeParse({
      description: 'No id provided',
      targetMetric: 'something'
    })
    expect(result.success).toBe(false)
  })

  it('rejects empty string id', () => {
    const result = KeyResultSchema.safeParse({
      id: '',
      description: 'Empty id',
      targetMetric: 'something'
    })
    expect(result.success).toBe(false)
  })

  it('rejects missing description', () => {
    const result = KeyResultSchema.safeParse({
      id: 'kr-no-desc',
      targetMetric: 'something'
    })
    expect(result.success).toBe(false)
  })

  it('rejects empty string description', () => {
    const result = KeyResultSchema.safeParse({
      id: 'kr-empty-desc',
      description: '',
      targetMetric: 'something'
    })
    expect(result.success).toBe(false)
  })
})

// ---------------------------------------------------------------------------
// Group 3: ObjectiveSchema — positive parse
// ---------------------------------------------------------------------------

describe('ObjectiveSchema — positive parse', () => {
  it('accepts a fully-populated objective with measurable outcomes', () => {
    const result = ObjectiveSchema.safeParse({
      id: 'goal-grow-arr-q1-2026',
      order: 10,
      description: 'Grow annual recurring revenue by 50% in Q1 2026',
      periodStart: '2026-01-01',
      periodEnd: '2026-03-31',
      keyResults: [
        {
          id: 'kr-arr',
          description: 'Reach $500k ARR',
          targetMetric: 'annual recurring revenue',
          currentValue: 330000,
          targetValue: 500000
        }
      ]
    })
    expect(result.success).toBe(true)
    if (result.success) {
      expect(result.data.id).toBe('goal-grow-arr-q1-2026')
      expect(result.data.keyResults).toHaveLength(1)
      expect(result.data.keyResults[0].id).toBe('kr-arr')
    }
  })

  it('accepts a minimal objective — no measurable outcomes', () => {
    const result = ObjectiveSchema.safeParse({
      id: 'goal-minimal',
      order: 10,
      description: 'Establish brand presence in new market',
      periodStart: '2026-04-01',
      periodEnd: '2026-06-30'
    })
    expect(result.success).toBe(true)
    if (result.success) {
      expect(result.data.keyResults).toEqual([])
    }
  })

  it('keyResults defaults to empty array when omitted', () => {
    const result = ObjectiveSchema.safeParse({
      id: 'goal-defaults',
      order: 10,
      description: 'Launch new product line',
      periodStart: '2026-01-01',
      periodEnd: '2026-12-31'
    })
    expect(result.success).toBe(true)
    if (result.success) {
      expect(result.data.keyResults).toEqual([])
    }
  })
})

// ---------------------------------------------------------------------------
// Group 4: ObjectiveSchema — negative parse
// ---------------------------------------------------------------------------

describe('ObjectiveSchema — negative parse', () => {
  it('rejects missing id', () => {
    const result = ObjectiveSchema.safeParse({
      order: 10,
      description: 'No id provided',
      periodStart: '2026-01-01',
      periodEnd: '2026-12-31'
    })
    expect(result.success).toBe(false)
  })

  it('rejects empty string id', () => {
    const result = ObjectiveSchema.safeParse({
      id: '',
      order: 10,
      description: 'Empty id',
      periodStart: '2026-01-01',
      periodEnd: '2026-12-31'
    })
    expect(result.success).toBe(false)
  })

  it('rejects missing description', () => {
    const result = ObjectiveSchema.safeParse({
      id: 'goal-no-desc',
      order: 10,
      periodStart: '2026-01-01',
      periodEnd: '2026-12-31'
    })
    expect(result.success).toBe(false)
  })

  it('rejects empty string description', () => {
    const result = ObjectiveSchema.safeParse({
      id: 'goal-empty-desc',
      order: 10,
      description: '',
      periodStart: '2026-01-01',
      periodEnd: '2026-12-31'
    })
    expect(result.success).toBe(false)
  })

  it('rejects periodStart with wrong date format (non-ISO string)', () => {
    const result = ObjectiveSchema.safeParse({
      id: 'goal-bad-start',
      order: 10,
      description: 'Bad date format',
      periodStart: '01/01/2026',
      periodEnd: '2026-12-31'
    })
    expect(result.success).toBe(false)
  })

  it('rejects periodEnd with wrong date format (non-ISO string)', () => {
    const result = ObjectiveSchema.safeParse({
      id: 'goal-bad-end',
      order: 10,
      description: 'Bad date format',
      periodStart: '2026-01-01',
      periodEnd: 'December 31, 2026'
    })
    expect(result.success).toBe(false)
  })

  it('rejects periodStart as a Date object (must be ISO string)', () => {
    const result = ObjectiveSchema.safeParse({
      id: 'goal-date-obj',
      order: 10,
      description: 'Date object not allowed',
      periodStart: new Date('2026-01-01'),
      periodEnd: '2026-12-31'
    })
    expect(result.success).toBe(false)
  })
})

// ---------------------------------------------------------------------------
// Group 5: GoalsDomainSchema — structural
// ---------------------------------------------------------------------------

describe('GoalsDomainSchema — structural', () => {
  it('accepts an empty goals record', () => {
    const result = GoalsDomainSchema.safeParse({})
    expect(result.success).toBe(true)
  })

  it('defaults goals to empty record when key is omitted', () => {
    const result = GoalsDomainSchema.safeParse({})
    expect(result.success).toBe(true)
    if (result.success) {
      expect(result.data).toEqual({})
    }
  })

  it('DEFAULT_ORGANIZATION_MODEL_GOALS constant matches schema parse of empty object', () => {
    const result = GoalsDomainSchema.safeParse({})
    expect(result.success).toBe(true)
    if (result.success) {
      expect(result.data).toEqual(DEFAULT_ORGANIZATION_MODEL_GOALS)
    }
  })

  it('accepts multiple valid objectives', () => {
    const result = GoalsDomainSchema.safeParse({
      'goal-a': {
        id: 'goal-a',
        order: 10,
        description: 'First goal',
        periodStart: '2026-01-01',
        periodEnd: '2026-03-31'
      },
      'goal-b': {
        id: 'goal-b',
        order: 20,
        description: 'Second goal',
        periodStart: '2026-04-01',
        periodEnd: '2026-06-30'
      }
    })
    expect(result.success).toBe(true)
    if (result.success) {
      expect(Object.keys(result.data)).toHaveLength(2)
    }
  })
})

// ---------------------------------------------------------------------------
// Group 6: Plain-language field names assertion
// Schema must NOT expose OKR jargon as a top-level property name.
// Field name `keyResults` is kept for tooling compat but "okr" must not appear.
// ---------------------------------------------------------------------------

describe('ObjectiveSchema — plain-language field names (no OKR jargon exposed)', () => {
  it('parsed objective has expected field names including keyResults for tooling compat', () => {
    const result = ObjectiveSchema.safeParse({
      id: 'goal-plain',
      order: 10,
      description: 'Expand into new markets',
      periodStart: '2026-01-01',
      periodEnd: '2026-12-31',
      keyResults: []
    })
    expect(result.success).toBe(true)
    if (result.success) {
      const keys = Object.keys(result.data)
      expect(keys).toContain('id')
      expect(keys).toContain('description')
      expect(keys).toContain('periodStart')
      expect(keys).toContain('periodEnd')
      expect(keys).toContain('keyResults')
      // No top-level property named "okr" — user-facing labels use "goals"
      expect(keys).not.toContain('okr')
    }
  })
})

// ---------------------------------------------------------------------------
// Group 7: Period-range validation via resolveOrganizationModel
// ---------------------------------------------------------------------------

describe('resolveOrganizationModel — goals period-range validation', () => {
  it('passes when periodEnd is strictly after periodStart', () => {
    expect(() =>
      resolveOrganizationModel({
        goals: {
          'goal-valid': {
            id: 'goal-valid',
            order: 10,
            description: 'Valid date range',
            periodStart: '2026-01-01',
            periodEnd: '2026-12-31'
          }
        }
      })
    ).not.toThrow()
  })

  it('passes when periodEnd is the next day after periodStart', () => {
    expect(() =>
      resolveOrganizationModel({
        goals: {
          'goal-one-day': {
            id: 'goal-one-day',
            order: 10,
            description: 'Single day range',
            periodStart: '2026-06-01',
            periodEnd: '2026-06-02'
          }
        }
      })
    ).not.toThrow()
  })

  it('throws when periodEnd equals periodStart', () => {
    expect(() =>
      resolveOrganizationModel({
        goals: {
          'goal-same-date': {
            id: 'goal-same-date',
            order: 10,
            description: 'Same start and end',
            periodStart: '2026-03-01',
            periodEnd: '2026-03-01'
          }
        }
      })
    ).toThrow()
  })

  it('throws when periodEnd is before periodStart', () => {
    expect(() =>
      resolveOrganizationModel({
        goals: {
          'goal-reversed': {
            id: 'goal-reversed',
            order: 10,
            description: 'Reversed date range',
            periodStart: '2026-12-31',
            periodEnd: '2026-01-01'
          }
        }
      })
    ).toThrow()
  })

  it('throws with a message referencing periodEnd path', () => {
    let errorMessage = ''
    try {
      resolveOrganizationModel({
        goals: {
          'goal-bad-range': {
            id: 'goal-bad-range',
            order: 10,
            description: 'Bad range for error message check',
            periodStart: '2026-06-01',
            periodEnd: '2026-01-01'
          }
        }
      })
    } catch (e) {
      errorMessage = String(e)
    }
    expect(errorMessage).toContain('periodEnd')
  })

  it('passes when goals record is empty (no ranges to validate)', () => {
    expect(() =>
      resolveOrganizationModel({
        goals: {}
      })
    ).not.toThrow()
  })
})

// ---------------------------------------------------------------------------
// Group 8: Integration — resolveOrganizationModel general
// ---------------------------------------------------------------------------

describe('resolveOrganizationModel — goals domain integration', () => {
  it('omitting goals key entirely resolves to default empty goals', () => {
    const model = resolveOrganizationModel({})
    expect(model.goals).toBeDefined()
    expect(Object.keys(model.goals)).toHaveLength(0)
  })

  it('merges partial goals override and preserves empty goals default', () => {
    const model = resolveOrganizationModel({ goals: {} })
    expect(Object.keys(model.goals)).toHaveLength(0)
  })

  it('goals domain is registered after roles — both present in resolved model', () => {
    const model = resolveOrganizationModel({
      roles: { 'role-ceo': { id: 'role-ceo', order: 10, title: 'CEO' } },
      goals: {
        'goal-grow': {
          id: 'goal-grow',
          order: 10,
          description: 'Grow the business',
          periodStart: '2026-01-01',
          periodEnd: '2026-12-31'
        }
      }
    })
    expect(Object.keys(model.roles)).toHaveLength(1)
    expect(Object.keys(model.goals)).toHaveLength(1)
    expect(model.goals['goal-grow'].id).toBe('goal-grow')
  })

  it('does not bleed goals changes into other top-level domains', () => {
    const model = resolveOrganizationModel({
      goals: {
        'goal-isolated': {
          id: 'goal-isolated',
          order: 10,
          description: 'Should not affect other domains',
          periodStart: '2026-01-01',
          periodEnd: '2026-06-30'
        }
      }
    })
    expect(model.identity).toBeDefined()
    expect(model.customers).toBeDefined()
    expect(model.offerings).toBeDefined()
    expect(model.roles).toBeDefined()
    expect(model.systems).toBeDefined()
  })

  it('resolved objective carries keyResults with defaulted currentValue', () => {
    const model = resolveOrganizationModel({
      goals: {
        'goal-with-kr': {
          id: 'goal-with-kr',
          order: 10,
          description: 'Goal with measurable outcomes',
          periodStart: '2026-01-01',
          periodEnd: '2026-12-31',
          keyResults: [
            {
              id: 'kr-1',
              description: 'Hit revenue target',
              targetMetric: 'monthly revenue',
              targetValue: 50000
            }
          ]
        }
      }
    })
    const objective = model.goals['goal-with-kr']
    expect(objective.keyResults).toHaveLength(1)
    expect(objective.keyResults[0].currentValue).toBe(0)
    expect(objective.keyResults[0].targetValue).toBe(50000)
  })
})
