"""
Tests for koci CLI tool.
"""

import os
import tempfile
from pathlib import Path

import pytest
from koci.main import KociTest
from koci.core import (
    parse_pipeline,
    parse_pipeline_string,
    Pipeline,
    Stage,
    Job,
    ParserError,
    ValidationError,
)
from koci.core.expressions import (
    ExecutionContext,
    evaluate_when,
    evaluate_if,
    evaluate_condition,
)


# Sample pipeline YAML for testing
SAMPLE_PIPELINE = """
version: "1"
name: test-pipeline

stages:
  - build
  - test

jobs:
  compile:
    stage: build
    image: node:20
    steps:
      - name: Install
        run: npm install
      - name: Build
        run: npm run build
    artifacts:
      paths:
        - dist/

  unit-tests:
    stage: test
    image: node:20
    needs: [compile]
    steps:
      - run: npm test
    artifacts:
      consume: [compile]
"""


class TestKociCLI:
    """Test CLI commands."""

    def test_koci_help(self):
        """Test koci without any subcommands."""
        with KociTest() as app:
            app.run()
            assert app.exit_code == 0

    def test_koci_debug(self):
        """Test that debug mode is functional."""
        argv = ['--debug']
        with KociTest(argv=argv) as app:
            app.run()
            assert app.debug is True

    def test_validate_with_file(self):
        """Test validate command with a pipeline file."""
        with tempfile.NamedTemporaryFile(mode='w', suffix='.yml', delete=False) as f:
            f.write(SAMPLE_PIPELINE)
            f.flush()

            try:
                argv = ['validate', '-f', f.name]
                with KociTest(argv=argv) as app:
                    app.run()
                    assert app.exit_code == 0
            finally:
                os.unlink(f.name)

    def test_info_command(self):
        """Test info command."""
        with tempfile.NamedTemporaryFile(mode='w', suffix='.yml', delete=False) as f:
            f.write(SAMPLE_PIPELINE)
            f.flush()

            try:
                argv = ['info', '-f', f.name]
                with KociTest(argv=argv) as app:
                    app.run()
                    assert app.exit_code == 0
            finally:
                os.unlink(f.name)


class TestPipelineParser:
    """Test pipeline parsing."""

    def test_parse_simple_pipeline(self):
        """Test parsing a simple pipeline."""
        pipeline = parse_pipeline_string(SAMPLE_PIPELINE)

        assert pipeline.name == 'test-pipeline'
        assert pipeline.version == '1'
        assert len(pipeline.stages) == 2
        assert pipeline.stages[0].name == 'build'
        assert pipeline.stages[1].name == 'test'

    def test_parse_jobs(self):
        """Test parsing jobs."""
        pipeline = parse_pipeline_string(SAMPLE_PIPELINE)

        jobs = pipeline.get_all_jobs()
        assert len(jobs) == 2

        compile_job = pipeline.get_job('compile')
        assert compile_job is not None
        assert compile_job.image == 'node:20'
        assert len(compile_job.steps) == 2
        assert compile_job.artifacts.paths == ['dist/']

    def test_parse_dependencies(self):
        """Test parsing job dependencies."""
        pipeline = parse_pipeline_string(SAMPLE_PIPELINE)

        test_job = pipeline.get_job('unit-tests')
        assert test_job is not None
        assert test_job.needs == ['compile']
        assert test_job.artifacts.consume == ['compile']

    def test_invalid_yaml(self):
        """Test parsing invalid YAML."""
        with pytest.raises(ParserError):
            parse_pipeline_string("invalid: yaml: content: [")

    def test_missing_stages(self):
        """Test validation with missing stages."""
        invalid_pipeline = """
version: "1"
name: test

jobs:
  build:
    stage: build
    steps:
      - run: echo hello
"""
        with pytest.raises(ValidationError):
            parse_pipeline_string(invalid_pipeline)


class TestExpressions:
    """Test expression evaluation."""

    def test_simple_when_branch(self):
        """Test simple branch condition."""
        ctx = ExecutionContext(branch='main')

        assert evaluate_when("branch == 'main'", ctx) is True
        assert evaluate_when("branch == 'develop'", ctx) is False
        assert evaluate_when("branch != 'develop'", ctx) is True

    def test_when_pattern_match(self):
        """Test pattern matching in when conditions."""
        ctx = ExecutionContext(branch='feature/test')

        assert evaluate_when("branch =~ 'feature/*'", ctx) is True
        assert evaluate_when("branch =~ 'release/*'", ctx) is False

    def test_when_always_never(self):
        """Test always and never keywords."""
        ctx = ExecutionContext()

        assert evaluate_when("always", ctx) is True
        assert evaluate_when("never", ctx) is False

    def test_when_compound(self):
        """Test compound conditions."""
        ctx = ExecutionContext(branch='main', tag='v1.0.0')

        assert evaluate_when("branch == 'main' && tag =~ 'v*'", ctx) is True
        assert evaluate_when("branch == 'develop' || tag =~ 'v*'", ctx) is True
        assert evaluate_when("branch == 'develop' && tag =~ 'v*'", ctx) is False

    def test_if_success_failure(self):
        """Test success() and failure() functions."""
        # All jobs succeeded
        ctx = ExecutionContext(job_results={'build': 'success', 'test': 'success'})
        assert evaluate_if("${{ success() }}", ctx) is True
        assert evaluate_if("${{ failure() }}", ctx) is False

        # One job failed
        ctx = ExecutionContext(job_results={'build': 'success', 'test': 'failed'})
        assert evaluate_if("${{ success() }}", ctx) is False
        assert evaluate_if("${{ failure() }}", ctx) is True

    def test_if_env_comparison(self):
        """Test environment variable comparisons."""
        ctx = ExecutionContext(env={'DEBUG': 'true', 'ENV': 'production'})

        assert evaluate_if("${{ env.DEBUG == 'true' }}", ctx) is True
        assert evaluate_if("${{ env.ENV == 'production' }}", ctx) is True
        assert evaluate_if("${{ env.ENV != 'staging' }}", ctx) is True

    def test_combined_conditions(self):
        """Test combined when and if conditions."""
        ctx = ExecutionContext(
            branch='main',
            env={'DEPLOY': 'true'},
            job_results={'build': 'success'},
        )

        assert evaluate_condition(
            when="branch == 'main'",
            condition="${{ success() }}",
            context=ctx,
        ) is True

        assert evaluate_condition(
            when="branch == 'develop'",
            condition="${{ success() }}",
            context=ctx,
        ) is False


class TestMatrixExpansion:
    """Test matrix job expansion."""

    def test_simple_matrix(self):
        """Test simple matrix expansion."""
        pipeline_yaml = """
version: "1"
name: test

stages:
  - test

jobs:
  test:
    stage: test
    image: python:3.11
    matrix:
      python: ["3.9", "3.10", "3.11"]
    steps:
      - run: python --version
"""
        pipeline = parse_pipeline_string(pipeline_yaml)
        job = pipeline.get_job('test')

        expanded = job.get_expanded_jobs()
        assert len(expanded) == 3

    def test_multi_dimension_matrix(self):
        """Test multi-dimensional matrix expansion."""
        pipeline_yaml = """
version: "1"
name: test

stages:
  - test

jobs:
  test:
    stage: test
    image: python:3.11
    matrix:
      python: ["3.9", "3.10"]
      os: ["linux", "macos"]
    steps:
      - run: echo test
"""
        pipeline = parse_pipeline_string(pipeline_yaml)
        job = pipeline.get_job('test')

        expanded = job.get_expanded_jobs()
        assert len(expanded) == 4  # 2 python × 2 os


class TestExporters:
    """Test CI platform exporters."""

    def test_github_actions_export(self):
        """Test GitHub Actions export."""
        from koci.exporters.github_actions import GitHubActionsExporter

        pipeline = parse_pipeline_string(SAMPLE_PIPELINE)
        exporter = GitHubActionsExporter()
        output = exporter.export(pipeline)

        assert 'name: test-pipeline' in output
        assert 'runs-on: ubuntu-latest' in output
        assert 'actions/checkout@v4' in output
        assert 'actions/upload-artifact@v4' in output

    def test_jenkins_export(self):
        """Test Jenkins export."""
        from koci.exporters.jenkins import JenkinsExporter

        pipeline = parse_pipeline_string(SAMPLE_PIPELINE)
        exporter = JenkinsExporter()
        output = exporter.export(pipeline)

        assert 'pipeline {' in output
        assert "stage('compile')" in output
        assert "docker {" in output

    def test_gitlab_export(self):
        """Test GitLab CI export."""
        from koci.exporters.gitlab import GitLabExporter

        pipeline = parse_pipeline_string(SAMPLE_PIPELINE)
        exporter = GitLabExporter()
        output = exporter.export(pipeline)

        assert 'stages:' in output
        assert '- build' in output
        assert '- test' in output
        assert 'script:' in output
