import json
from pathlib import Path

import pytest

from dotpromptz.merge import merge_directory
from dotpromptz.yamlio import load_yaml


def _read_jsonl(path: Path) -> list[dict]:
    return [json.loads(line) for line in path.read_text(encoding='utf-8').splitlines() if line.strip()]


def test_merge_directory_default_non_recursive(tmp_path: Path) -> None:
    source_dir = tmp_path / 'yyy'
    source_dir.mkdir()
    (source_dir / 'a.json').write_text('{"id": 1}', encoding='utf-8')

    nested_dir = source_dir / 'sub'
    nested_dir.mkdir()
    (nested_dir / 'b.json').write_text('{"id": 2}', encoding='utf-8')

    output = merge_directory(source_dir)

    assert output == tmp_path / 'yyy.jsonl'
    assert _read_jsonl(output) == [{'id': 1}]


def test_merge_directory_pattern_recursive_glob(tmp_path: Path) -> None:
    source_dir = tmp_path / 'yyy'
    source_dir.mkdir()
    (source_dir / 'a.json').write_text('{"id": 1}', encoding='utf-8')

    nested_dir = source_dir / 'sub'
    nested_dir.mkdir()
    (nested_dir / 'b.json').write_text('{"id": 2}', encoding='utf-8')

    output = merge_directory(source_dir, pattern='**/*.json')

    assert output == tmp_path / 'yyy.jsonl'
    assert _read_jsonl(output) == [{'id': 1}, {'id': 2}]


def test_merge_directory_mixed_formats_raises(tmp_path: Path) -> None:
    source_dir = tmp_path / 'yyy'
    source_dir.mkdir()
    (source_dir / 'a.json').write_text('{"id": 1}', encoding='utf-8')
    (source_dir / 'b.yaml').write_text('id: 2\n', encoding='utf-8')

    with pytest.raises(ValueError, match='same format'):
        merge_directory(source_dir)


def test_merge_directory_schema_mismatch_raises(tmp_path: Path) -> None:
    source_dir = tmp_path / 'yyy'
    source_dir.mkdir()
    (source_dir / 'a.json').write_text('{"user": {"name": "alice"}}', encoding='utf-8')
    (source_dir / 'b.json').write_text('{"user": {"name": 1}}', encoding='utf-8')

    with pytest.raises(ValueError, match='Schema mismatch'):
        merge_directory(source_dir, pattern='*.json')


def test_merge_directory_yaml_plain_output_list(tmp_path: Path) -> None:
    source_dir = tmp_path / 'yyy'
    source_dir.mkdir()
    (source_dir / 'a.yaml').write_text('name: alice\nage: 1\n', encoding='utf-8')
    (source_dir / 'b.yaml').write_text('name: bob\nage: 2\n', encoding='utf-8')

    output = merge_directory(source_dir)

    assert output == tmp_path / 'yyy.yaml'
    assert load_yaml(output.read_text(encoding='utf-8')) == [
        {'name': 'alice', 'age': 1},
        {'name': 'bob', 'age': 2},
    ]


def test_merge_directory_txt_plain_output_jsonl(tmp_path: Path) -> None:
    source_dir = tmp_path / 'yyy'
    source_dir.mkdir()
    (source_dir / 'a.txt').write_text('hello', encoding='utf-8')
    (source_dir / 'b.txt').write_text('world', encoding='utf-8')

    output = merge_directory(source_dir)

    assert output == tmp_path / 'yyy.jsonl'
    assert _read_jsonl(output) == [{'text': 'hello'}, {'text': 'world'}]


def test_merge_directory_json_with_filename(tmp_path: Path) -> None:
    source_dir = tmp_path / 'yyy'
    source_dir.mkdir()
    (source_dir / 'a.json').write_text('{"id": 1}', encoding='utf-8')
    (source_dir / 'b.json').write_text('{"id": 2}', encoding='utf-8')

    output = merge_directory(source_dir, with_filename=True)

    assert output == tmp_path / 'yyy.jsonl'
    assert _read_jsonl(output) == [
        {'id': 1, 'FILENAME': 'a'},
        {'id': 2, 'FILENAME': 'b'},
    ]


def test_merge_directory_txt_with_filename(tmp_path: Path) -> None:
    source_dir = tmp_path / 'yyy'
    source_dir.mkdir()
    (source_dir / 'a.txt').write_text('hello', encoding='utf-8')

    output = merge_directory(source_dir, with_filename=True)

    assert output == tmp_path / 'yyy.jsonl'
    assert _read_jsonl(output) == [{'text': 'hello', 'FILENAME': 'a'}]


def test_merge_directory_output_exists_requires_force(tmp_path: Path) -> None:
    source_dir = tmp_path / 'yyy'
    source_dir.mkdir()
    (source_dir / 'a.json').write_text('{"id": 1}', encoding='utf-8')

    output = tmp_path / 'yyy.jsonl'
    output.write_text('old\n', encoding='utf-8')

    with pytest.raises(ValueError, match='already exists'):
        merge_directory(source_dir)

    merged_output = merge_directory(source_dir, force=True)

    assert merged_output == output
    assert _read_jsonl(output) == [{'id': 1}]


def test_merge_directory_pattern_matches_unsupported_extension_raises(tmp_path: Path) -> None:
    source_dir = tmp_path / 'yyy'
    source_dir.mkdir()
    (source_dir / 'a.md').write_text('# title', encoding='utf-8')

    with pytest.raises(ValueError, match='Unsupported file format'):
        merge_directory(source_dir, pattern='*.md')


def test_merge_directory_no_supported_files_raises(tmp_path: Path) -> None:
    source_dir = tmp_path / 'yyy'
    source_dir.mkdir()
    (source_dir / 'a.md').write_text('# title', encoding='utf-8')

    with pytest.raises(ValueError, match='No supported files found'):
        merge_directory(source_dir)


def test_merge_directory_empty_structured_list_raises(tmp_path: Path) -> None:
    source_dir = tmp_path / 'yyy'
    source_dir.mkdir()
    (source_dir / 'a.json').write_text('[]', encoding='utf-8')

    with pytest.raises(ValueError, match='at least one object'):
        merge_directory(source_dir)
