import { vi } from 'vitest'
import { GitLabEventFactory } from '../../../../factories/gitLabEvent.factory.js'
import { GitHubEventFactory } from '../../../../factories/gitHubEvent.factory.js'

vi.mock('../../../../../config/loader.js', () => ({
  loadConfig: vi.fn(() => ({
    user: {
      gitlabUsername: 'claude-reviewer',
      githubUsername: 'claude-reviewer',
    },
  })),
}))

import { loadConfig } from '../../../../../config/loader.js'
import {
  filterGitLabEvent,
  filterGitLabMrUpdate,
  filterGitLabMrClose,
  filterGitLabMrMerge,
  filterGitLabMrApprove,
  filterGitHubEvent,
  filterGitHubLabelEvent,
  filterGitHubPrClose,
  filterGitHubPrUpdate,
  REVIEW_TRIGGER_LABEL,
} from '@/modules/platform-integration/interface-adapters/controllers/webhook/eventFilter.js'

describe('filterGitLabEvent', () => {
  describe('when MR is opened with reviewer assigned', () => {
    it('should process when claude-reviewer is newly added as reviewer', () => {
      const event = GitLabEventFactory.createWithReviewerAdded('claude-reviewer')

      const result = filterGitLabEvent(event)

      expect(result.shouldProcess).toBe(true)
      expect(result.reason).toContain('claude-reviewer')
      if (result.shouldProcess) {
        expect(result.mergeRequestNumber).toBe(42)
        expect(result.projectPath).toBe('test-org/test-project')
      }
    })
  })

  describe('when MR is draft', () => {
    it('should not process draft MRs', () => {
      const event = GitLabEventFactory.createDraftMr()
      event.changes = {
        reviewers: {
          previous: [],
          current: [{ username: 'claude-reviewer' }],
        },
      }

      const result = filterGitLabEvent(event)

      expect(result.shouldProcess).toBe(false)
      expect(result.reason).toContain('draft')
    })
  })

  describe('when reviewer is not claude-reviewer', () => {
    it('should not process when different reviewer is added', () => {
      const event = GitLabEventFactory.createWithReviewerAdded('other-reviewer')

      const result = filterGitLabEvent(event)

      expect(result.shouldProcess).toBe(false)
      expect(result.reason).toContain('claude-reviewer')
    })
  })

  describe('when MR is not in opened state', () => {
    it('should not process closed MRs', () => {
      const event = GitLabEventFactory.createClosedMr()

      const result = filterGitLabEvent(event)

      expect(result.shouldProcess).toBe(false)
      expect(result.reason).toContain('closed')
    })

    it('should not process merged MRs', () => {
      const event = GitLabEventFactory.createMergedMr()

      const result = filterGitLabEvent(event)

      expect(result.shouldProcess).toBe(false)
      expect(result.reason).toContain('merged')
    })
  })

  describe('when object_kind is not merge_request', () => {
    it('should not process non-MR events', () => {
      const event = GitLabEventFactory.createMergeRequestEvent({
        object_kind: 'push' as 'merge_request',
      })

      const result = filterGitLabEvent(event)

      expect(result.shouldProcess).toBe(false)
      expect(result.reason).toContain('Not a merge request')
    })
  })

  describe('when reviewer was already assigned (no change)', () => {
    it('should not process when no reviewer change detected', () => {
      const event = GitLabEventFactory.createMergeRequestEvent({
        reviewers: [{ username: 'claude-reviewer', name: 'Claude' }],
      })

      const result = filterGitLabEvent(event)

      expect(result.shouldProcess).toBe(false)
      expect(result.reason).toContain('was not added as reviewer')
    })
  })

  describe('when reviewer was in previous and current', () => {
    it('should not trigger when reviewer was already present', () => {
      const event = GitLabEventFactory.createMergeRequestEvent({
        object_attributes: { action: 'update' },
        reviewers: [{ username: 'claude-reviewer', name: 'Claude' }],
        changes: {
          reviewers: {
            previous: [{ username: 'claude-reviewer' }],
            current: [{ username: 'claude-reviewer' }],
          },
        },
      })

      const result = filterGitLabEvent(event)

      expect(result.shouldProcess).toBe(false)
    })
  })
})

describe('filterGitLabMrUpdate', () => {
  describe('when MR receives update action', () => {
    it('should process update events for followup', () => {
      const event = GitLabEventFactory.createMrUpdate()

      const result = filterGitLabMrUpdate(event)

      expect(result.shouldProcess).toBe(true)
      if (result.shouldProcess) {
        expect(result.isFollowup).toBe(true)
      }
      expect(result.reason).toContain('updated')
    })
  })

  describe('when MR is draft', () => {
    it('should not process draft MR updates', () => {
      const event = GitLabEventFactory.createMergeRequestEvent({
        object_attributes: {
          action: 'update',
          draft: true,
        },
      })

      const result = filterGitLabMrUpdate(event)

      expect(result.shouldProcess).toBe(false)
      expect(result.reason).toContain('draft')
    })
  })

  describe('when action is not update', () => {
    it('should not process open actions', () => {
      const event = GitLabEventFactory.createMergeRequestEvent({
        object_attributes: { action: 'open' },
      })

      const result = filterGitLabMrUpdate(event)

      expect(result.shouldProcess).toBe(false)
      expect(result.reason).toContain('open')
    })
  })

  describe('when MR state is not opened', () => {
    it('should not process closed MR updates', () => {
      const event = GitLabEventFactory.createMergeRequestEvent({
        object_attributes: {
          state: 'closed',
          action: 'update',
        },
      })

      const result = filterGitLabMrUpdate(event)

      expect(result.shouldProcess).toBe(false)
      expect(result.reason).toContain('closed')
    })
  })
})

describe('filterGitLabMrClose', () => {
  describe('when MR is closed', () => {
    it('should detect closure for cleanup', () => {
      const event = GitLabEventFactory.createClosedMr()

      const result = filterGitLabMrClose(event)

      expect(result.shouldProcess).toBe(true)
      expect(result.reason).toContain('closed')
      if (result.shouldProcess) {
        expect(result.mergeRequestNumber).toBe(42)
      }
    })
  })

  describe('when action is not close', () => {
    it('should not process non-close actions', () => {
      const event = GitLabEventFactory.createMergeRequestEvent({
        object_attributes: { action: 'update' },
      })

      const result = filterGitLabMrClose(event)

      expect(result.shouldProcess).toBe(false)
    })
  })

  describe('when state is not closed', () => {
    it('should not process if state mismatch', () => {
      const event = GitLabEventFactory.createMergeRequestEvent({
        object_attributes: {
          action: 'close',
          state: 'opened',
        },
      })

      const result = filterGitLabMrClose(event)

      expect(result.shouldProcess).toBe(false)
    })
  })
})

describe('filterGitLabMrMerge', () => {
  describe('when MR is merged', () => {
    it('should detect merge event', () => {
      const event = GitLabEventFactory.createMergedMr()

      const result = filterGitLabMrMerge(event)

      expect(result.shouldProcess).toBe(true)
      expect(result.reason).toContain('merged')
      if (result.shouldProcess) {
        expect(result.mergeRequestNumber).toBe(42)
        expect(result.projectPath).toBe('test-org/test-project')
      }
    })
  })

  describe('when action is not merge', () => {
    it('should not process non-merge actions', () => {
      const event = GitLabEventFactory.createMergeRequestEvent({
        object_attributes: { action: 'update' },
      })

      const result = filterGitLabMrMerge(event)

      expect(result.shouldProcess).toBe(false)
      expect(result.reason).toContain('update')
    })
  })

  describe('when state is not merged', () => {
    it('should not process if state mismatch', () => {
      const event = GitLabEventFactory.createMergeRequestEvent({
        object_attributes: {
          action: 'merge',
          state: 'opened',
        },
      })

      const result = filterGitLabMrMerge(event)

      expect(result.shouldProcess).toBe(false)
      expect(result.reason).toContain('opened')
    })
  })
})

describe('filterGitLabMrApprove', () => {
  describe('when MR is approved', () => {
    it('should detect approval event', () => {
      const event = GitLabEventFactory.createApprovedMr()

      const result = filterGitLabMrApprove(event)

      expect(result.shouldProcess).toBe(true)
      expect(result.reason).toContain('approved')
      if (result.shouldProcess) {
        expect(result.mergeRequestNumber).toBe(42)
        expect(result.projectPath).toBe('test-org/test-project')
      }
    })
  })

  describe('when action is not approved', () => {
    it('should not process non-approved actions', () => {
      const event = GitLabEventFactory.createMergeRequestEvent({
        object_attributes: { action: 'update' },
      })

      const result = filterGitLabMrApprove(event)

      expect(result.shouldProcess).toBe(false)
      expect(result.reason).toContain('update')
    })
  })
})

describe('filterGitHubEvent', () => {
  describe('when PR has review_requested action with correct reviewer', () => {
    it('should process when claude-reviewer is requested', () => {
      const event = GitHubEventFactory.createReviewRequestedPr('claude-reviewer')

      const result = filterGitHubEvent(event)

      expect(result.shouldProcess).toBe(true)
      expect(result.reason).toContain('claude-reviewer')
      if (result.shouldProcess) {
        expect(result.mergeRequestNumber).toBe(123)
        expect(result.projectPath).toBe('test-owner/test-repo')
      }
    })
  })

  describe('when action is not review_requested', () => {
    it('should not process opened action', () => {
      const event = GitHubEventFactory.createPullRequestEvent({ action: 'opened' })

      const result = filterGitHubEvent(event)

      expect(result.shouldProcess).toBe(false)
      expect(result.reason).toContain('opened')
    })
  })

  describe('when PR is draft', () => {
    it('should not process draft PRs even with review requested', () => {
      const event = GitHubEventFactory.createPullRequestEvent({
        action: 'review_requested',
        requested_reviewer: { login: 'claude-reviewer' },
        pull_request: { draft: true },
      })

      const result = filterGitHubEvent(event)

      expect(result.shouldProcess).toBe(false)
      expect(result.reason).toContain('draft')
    })
  })

  describe('when requested reviewer is not claude-reviewer', () => {
    it('should not process when different reviewer requested', () => {
      const event = GitHubEventFactory.createReviewRequestedPr('other-reviewer')

      const result = filterGitHubEvent(event)

      expect(result.shouldProcess).toBe(false)
      expect(result.reason).toContain('other-reviewer')
    })
  })

  describe('when PR state is not open', () => {
    it('should not process closed PRs', () => {
      const event = GitHubEventFactory.createPullRequestEvent({
        action: 'review_requested',
        requested_reviewer: { login: 'claude-reviewer' },
        pull_request: { state: 'closed' },
      })

      const result = filterGitHubEvent(event)

      expect(result.shouldProcess).toBe(false)
      expect(result.reason).toContain('closed')
    })
  })
})

describe('filterGitHubPrClose', () => {
  describe('when PR is closed', () => {
    it('should detect closure for cleanup', () => {
      const event = GitHubEventFactory.createClosedPr()

      const result = filterGitHubPrClose(event)

      expect(result.shouldProcess).toBe(true)
      expect(result.reason).toContain('closed')
      if (result.shouldProcess) {
        expect(result.mergeRequestNumber).toBe(123)
      }
    })
  })

  describe('when action is not closed', () => {
    it('should not process non-closed actions', () => {
      const event = GitHubEventFactory.createPullRequestEvent({ action: 'opened' })

      const result = filterGitHubPrClose(event)

      expect(result.shouldProcess).toBe(false)
    })
  })

  describe('when state is not closed', () => {
    it('should not process if state mismatch', () => {
      const event = GitHubEventFactory.createPullRequestEvent({
        action: 'closed',
        pull_request: { state: 'open' },
      })

      const result = filterGitHubPrClose(event)

      expect(result.shouldProcess).toBe(false)
    })
  })
})

describe('filterGitHubLabelEvent', () => {
  describe('when needs-review label is added', () => {
    it('should process when needs-review label is added', () => {
      const event = GitHubEventFactory.createLabeledPr(REVIEW_TRIGGER_LABEL)

      const result = filterGitHubLabelEvent(event)

      expect(result.shouldProcess).toBe(true)
      expect(result.reason).toContain(REVIEW_TRIGGER_LABEL)
      if (result.shouldProcess) {
        expect(result.mergeRequestNumber).toBe(123)
        expect(result.projectPath).toBe('test-owner/test-repo')
      }
    })
  })

  describe('when different label is added', () => {
    it('should not process other labels', () => {
      const event = GitHubEventFactory.createLabeledPr('bug')

      const result = filterGitHubLabelEvent(event)

      expect(result.shouldProcess).toBe(false)
      expect(result.reason).toContain('bug')
      expect(result.reason).toContain(REVIEW_TRIGGER_LABEL)
    })
  })

  describe('when action is not labeled', () => {
    it('should not process non-labeled actions', () => {
      const event = GitHubEventFactory.createPullRequestEvent({ action: 'opened' })

      const result = filterGitHubLabelEvent(event)

      expect(result.shouldProcess).toBe(false)
      expect(result.reason).toContain('opened')
    })
  })

  describe('when PR is draft', () => {
    it('should not process draft PRs', () => {
      const event = GitHubEventFactory.createLabeledPr(REVIEW_TRIGGER_LABEL)
      event.pull_request.draft = true

      const result = filterGitHubLabelEvent(event)

      expect(result.shouldProcess).toBe(false)
      expect(result.reason).toContain('draft')
    })
  })

  describe('when PR is closed', () => {
    it('should not process closed PRs', () => {
      const event = GitHubEventFactory.createLabeledPr(REVIEW_TRIGGER_LABEL)
      event.pull_request.state = 'closed'

      const result = filterGitHubLabelEvent(event)

      expect(result.shouldProcess).toBe(false)
      expect(result.reason).toContain('closed')
    })
  })
})

describe('filterGitHubPrUpdate', () => {
  describe('when PR receives synchronize action on open non-draft PR', () => {
    it('should process synchronize events as followup', () => {
      const event = GitHubEventFactory.createPullRequestEvent({
        action: 'synchronize',
        pull_request: { state: 'open', draft: false },
      })

      const result = filterGitHubPrUpdate(event)

      expect(result.shouldProcess).toBe(true)
      if (result.shouldProcess) {
        expect(result.isFollowup).toBe(true)
        expect(result.mergeRequestNumber).toBe(123)
        expect(result.projectPath).toBe('test-owner/test-repo')
      }
    })
  })

  describe('when PR state is closed', () => {
    it('should not process synchronize events on closed PRs', () => {
      const event = GitHubEventFactory.createPullRequestEvent({
        action: 'synchronize',
        pull_request: { state: 'closed', draft: false },
      })

      const result = filterGitHubPrUpdate(event)

      expect(result.shouldProcess).toBe(false)
      expect(result.reason).toContain('not open')
    })
  })

  describe('when PR is a draft', () => {
    it('should not process synchronize events on draft PRs', () => {
      const event = GitHubEventFactory.createPullRequestEvent({
        action: 'synchronize',
        pull_request: { state: 'open', draft: true },
      })

      const result = filterGitHubPrUpdate(event)

      expect(result.shouldProcess).toBe(false)
      expect(result.reason).toContain('draft')
    })
  })

  describe('when action is not synchronize', () => {
    it('should not process opened action', () => {
      const event = GitHubEventFactory.createPullRequestEvent({ action: 'opened' })

      const result = filterGitHubPrUpdate(event)

      expect(result.shouldProcess).toBe(false)
      expect(result.reason).toContain('not synchronize')
    })
  })
})

describe('filterGitLabEvent with empty gitlabUsername', () => {
  it('should not process when gitlabUsername is empty', () => {
    vi.mocked(loadConfig).mockReturnValueOnce({
      user: { gitlabUsername: '', githubUsername: 'claude-reviewer' },
    } as ReturnType<typeof loadConfig>)
    const event = GitLabEventFactory.createWithReviewerAdded('claude-reviewer')

    const result = filterGitLabEvent(event)

    expect(result.shouldProcess).toBe(false)
  })
})

describe('filterGitHubEvent with empty githubUsername', () => {
  it('should not process when githubUsername is empty', () => {
    vi.mocked(loadConfig).mockReturnValueOnce({
      user: { gitlabUsername: 'claude-reviewer', githubUsername: '' },
    } as ReturnType<typeof loadConfig>)
    const event = GitHubEventFactory.createReviewRequestedPr('claude-reviewer')

    const result = filterGitHubEvent(event)

    expect(result.shouldProcess).toBe(false)
  })
})

describe('FilterResult type narrowing', () => {
  it('should have all required fields when shouldProcess is true', () => {
    const event = GitHubEventFactory.createReviewRequestedPr('claude-reviewer')

    const result = filterGitHubEvent(event)

    if (result.shouldProcess) {
      expect(result.mergeRequestNumber).toBe(123)
      expect(result.projectPath).toBe('test-owner/test-repo')
      expect(result.mergeRequestUrl).toBeDefined()
      expect(result.sourceBranch).toBeDefined()
      expect(result.targetBranch).toBeDefined()
    }
  })
})
