from pathlib import Path
import pytest
import hashlib

from modules.scanner import listar_ficheiros, listar_ficheiros_unicos


def criar_estrutura_testes(base_dir):
    """Cria uma estrutura de pastas e ficheiros para os testes"""
    (base_dir / "imagem.jpg").write_text("conteudo imagem")
    (base_dir / "documento.").write_text("conteudo documento")
    (base_dir / ".ficheiro_oculto.txt").write_text("conteudo ficheiro oculto")
    (base_dir / "subpasta").mkdir()
    (base_dir / "subpasta" / "video.mp4").write_text("conteudo video")


def test_listar_ficheiros_todos(tmp_path):
    criar_estrutura_testes(tmp_path)
    ficheiros = listar_ficheiros(str(tmp_path))
    assert len(ficheiros) == 4  # 4 ficheiros no total (3 normais + 1 oculto)


def test_listar_ficheiros_excluir_ocultos(tmp_path):
    criar_estrutura_testes(tmp_path)
    ficheiros = listar_ficheiros(str(tmp_path), incluir_ocultos=False)
    assert len(ficheiros) == 3
    assert not any(Path(f).name.startswith(".") for f in ficheiros)


def test_listar_ficheiros_filtrar_por_extensao(tmp_path):
    criar_estrutura_testes(tmp_path)
    ficheiros = listar_ficheiros(str(tmp_path), extensoes=[".jpg", "mp4"])
    assert len(ficheiros) == 2
    assert all(Path(f).suffix.lower() in [".jpg", ".mp4"] for f in ficheiros)


def test_listar_ficheiros_extensoes_sem_ponto(tmp_path):
    criar_estrutura_testes(tmp_path)
    ficheiros = listar_ficheiros(str(tmp_path), extensoes=["jpg", "mp4"])
    assert len(ficheiros) == 2
    assert all(Path(f).suffix.lower() in [".jpg", ".mp4"] for f in ficheiros)


def test_listar_ficheiros_directorio_invalido():
    with pytest.raises(FileNotFoundError):
        listar_ficheiros("direntório_inexistente")


def test_listar_ficheiros_nao_e_directorio(tmp_path):
    ficheiro = tmp_path / "arquivo.txt"
    ficheiro.write_text("conteudo")
    with pytest.raises(NotADirectoryError):
        listar_ficheiros(str(ficheiro))


def test_listar_ficheiros_vazio(tmp_path):
    ficheiros = listar_ficheiros(str(tmp_path))
    assert ficheiros == []


def test_listar_ficheiros_simples(tmp_path):
    # criar ficheiros
    ficheiro1 = tmp_path / "ficheiro1.txt"
    ficheiro1.write_text("texto")

    ficheiro2 = tmp_path / "ficheiro2.mpg"
    ficheiro2.write_text("ficheiro de video")

    ficheiros = listar_ficheiros(str(tmp_path))
    nomes = [Path(f).name for f in ficheiros]

    assert len(ficheiros) == 2
    assert "ficheiro1.txt" in nomes
    assert "ficheiro2.mpg" in nomes


def test_listar_ficheiros_em_subpastas(tmp_path):
    subpasta = tmp_path / "subpasta"
    subpasta.mkdir()

    ficheiro_sub = subpasta / "ficheiro_sub.txt"
    ficheiro_sub.write_text("conteudo na subpasta")

    ficheiros = listar_ficheiros(str(tmp_path))
    nome = [Path(f).name for f in ficheiros]

    assert len(ficheiros) == 1
    assert "ficheiro_sub.txt" in nome


def test_listar_ficheiros_ocultos(tmp_path):
    ficheiro_oculto = tmp_path / ".oculto.txt"
    ficheiro_oculto.write_text("conteudo")

    ficheiros = listar_ficheiros(str(tmp_path))
    nomes = [Path(f).name for f in ficheiros]

    assert ".oculto.txt" in nomes


def test_listar_ficheiros_nomes_especiais(tmp_path):
    ficheiro_especial = tmp_path / "ficheiro_éxamplo-çãõ.txt"
    ficheiro_especial.write_text("conteudo")

    ficheiros = listar_ficheiros(str(tmp_path))
    nomes = [Path(f).name for f in ficheiros]

    assert "ficheiro_éxamplo-çãõ.txt" in nomes


def criar_ficheiro(caminho, conteudo="conteudo"):
    caminho.write_text(conteudo)


def sha256_do_texto(texto: str) -> str:
    return hashlib.sha256(texto.encode()).hexdigest()


def test_listar_ficheiros_unicos_sem_duplicados(tmp_path):
    # criar tres ficheiros diferentes
    f1 = tmp_path / "a.txt"
    f2 = tmp_path / "b.txt"
    f3 = tmp_path / "c.txt"
    criar_ficheiro(f1, "conteudo1")
    criar_ficheiro(f2, "conteudo2")
    criar_ficheiro(f3, "conteudo3")

    resultado = listar_ficheiros_unicos(str(tmp_path))
    assert isinstance(resultado, dict)
    assert len(resultado) == 3  # cada ficheiro com assinatura unica
    caminhos_listados = [c for caminhos in resultado.values()
                         for c in caminhos]
    assert set(caminhos_listados) == {str(f1), str(f2), str(f3)}


def test_listar_ficheiros_unicos_com_duplicados(tmp_path):
    # criar dois ficheiros iguais e um diferente
    f1 = tmp_path / "x.txt"
    f2 = tmp_path / "y.txt"
    f3 = tmp_path / "z.txt"
    criar_ficheiro(f1, "conteudo_duplicado")
    criar_ficheiro(f2, "conteudo_duplicado")
    criar_ficheiro(f3, "conteudo_unico")

    resultado = listar_ficheiros_unicos(str(tmp_path))
    assert isinstance(resultado, dict)
    assert len(resultado) == 2  # apenas duas assinaturas unicas

    # verificar que os ficheiros duplicados estão no mesmo grupo
    duplicados = [caminhos for caminhos in resultado.values()
                  if len(caminhos) > 1]
    assert len(duplicados) == 1
    assert set(duplicados[0]) == {str(f1), str(f2)}


def test_listar_ficheiros_unicos_respeita_incluir_ocultos(tmp_path):
    f1 = tmp_path / "normal.txt"
    f2 = tmp_path / ".oculto.txt"
    criar_ficheiro(f1, "visivel")
    criar_ficheiro(f2, "oculto")

    # Testar com ocultos incluídos
    resultado = listar_ficheiros_unicos(str(tmp_path), incluir_ocultos=True)
    caminhos_listados = [c for caminhos in resultado.values()
                         for c in caminhos]
    assert set(caminhos_listados) == {str(f1), str(f2)}

    # Testar com ocultos excluídos
    resultado_sem_ocultos = listar_ficheiros_unicos(
        str(tmp_path), incluir_ocultos=False
    )
    caminhos_listados_sem = [
        c for caminhos in resultado_sem_ocultos.values() for c in caminhos
    ]
    assert str(f2) not in caminhos_listados_sem
    assert str(f1) in caminhos_listados_sem


def test_listar_ficheiros_unicos_respeita_extensoes(tmp_path):
    f1 = tmp_path / "a.txt"
    f2 = tmp_path / "b.jpg"
    criar_ficheiro(f1)
    criar_ficheiro(f2)

    # Só listar ficheiros .txt
    resultado = listar_ficheiros_unicos(str(tmp_path), extensoes=[".txt"])
    caminhos_listados = [c for caminhos in resultado.values()
                         for c in caminhos]

    assert str(f1) in caminhos_listados
    assert str(f2) not in caminhos_listados
