"""Regresiones del catalogo PHP con la disposicion publicada de V-COMPUTACION."""

from pathlib import Path
import json
import shutil
import subprocess
import tempfile
import unittest
from contextlib import contextmanager
from unittest.mock import patch

ROOT = Path(__file__).resolve().parents[1]
PHP = shutil.which('php')
TEST_ROOT = ROOT.parent/'tmp/catalogo_local_pruebas'


@contextmanager
def workspace_fixture():
    TEST_ROOT.mkdir(parents=True, exist_ok=True)
    temporary = tempfile.TemporaryDirectory(dir=TEST_ROOT)
    assert Path(temporary.name).resolve().is_relative_to(TEST_ROOT.resolve())
    try:
        yield temporary.name
    finally:
        assert Path(temporary.name).resolve().is_relative_to(TEST_ROOT.resolve())
        temporary.cleanup()


RUNNER = '''$_GET = json_decode($argv[1], true);
$_SERVER['SCRIPT_NAME'] = '/teorias/pdf/V-COMPUTACION/index.php';
register_shutdown_function(function () {
    $status = http_response_code();
    fwrite(STDERR, "HTTP_STATUS=" . ($status === false ? 200 : $status));
});
include $argv[2];'''


@unittest.skipUnless(PHP, 'PHP no disponible')
class CatalogoLocal(unittest.TestCase):
    def render(self, root, **query):
        result = subprocess.run([PHP, '-r', RUNNER, json.dumps(query), str(root/'index.php')],
                                capture_output=True, check=True)
        stderr = result.stderr.decode('utf-8', errors='replace')
        self.assertNotIn('Warning:', stderr)
        self.assertNotIn('Fatal error:', stderr)
        return result.stdout, stderr

    def fixture(self, root, nested=True):
        shutil.copyfile(ROOT/'index.php', root/'index.php')
        pdfs = root/'pdfs' if nested else root
        pdfs.mkdir(exist_ok=True)
        for name in ('ES_52_01_ConceptosVcomputacion', 'ES_52_02_ConceptosVcomputacion',
                     'EN_52_01_ConceptosVcomputacion'):
            (pdfs/(name+'.pdf')).write_bytes(b'%PDF-1.4\n'+name.encode())
        (root/'ES_52_01_ConceptosVcomputacion.md').write_text(
            'CONCEPTOS\n\nARTÍCULO 01 — Primera ficha\n', encoding='utf-8')
        (root/'img').mkdir()
        (root/'img/ES_52_01_ConceptosVcomputacion.png').write_bytes(b'fixture')

    def test_archivos_reales_cuatro_idiomas(self):
        for lang in ('es', 'en', 'fr', 'it'):
            with self.subTest(lang=lang):
                data, status = self.render(ROOT, idioma=lang)
                text = data.decode('utf-8')
                self.assertEqual(text.count('<article class="card">'), 9)
                self.assertIn(f'pdfs/{lang.upper()}_52_01_ConceptosVcomputacion.pdf', text)
                self.assertIn('HTTP_STATUS=200', status)

    def test_carpetas_y_colisiones(self):
        with workspace_fixture() as name:
            root = Path(name); self.fixture(root)
            data, _ = self.render(root, idioma='es')
            text = data.decode()
            self.assertEqual(text.count('<article class="card">'), 2)
            self.assertIn('52.01 - Primera ficha', text)
            self.assertIn('pdfs/ES_52_02_ConceptosVcomputacion.pdf', text)

    def test_lector_md_imagen_y_pdf_directo(self):
        with workspace_fixture() as name:
            root = Path(name); self.fixture(root)
            filename = 'ES_52_01_ConceptosVcomputacion.pdf'
            data, status = self.render(root, idioma='es', doc=filename)
            self.assertIn('Primera ficha', data.decode())
            self.assertIn(b'img/ES_52_01_ConceptosVcomputacion.png', data)
            self.assertIn(b'btnPlay', data)
            self.assertIn('HTTP_STATUS=200', status)
            data, _ = self.render(root, doc=filename, directo='1')
            self.assertEqual(data, (root/'pdfs'/filename).read_bytes())

    def test_pdf_raiz_sigue_funcionando(self):
        with workspace_fixture() as name:
            root = Path(name); self.fixture(root, nested=False)
            data, _ = self.render(root, idioma='es')
            self.assertEqual(data.count(b'<article class="card">'), 2)
            self.assertIn(b'href="ES_52_01_ConceptosVcomputacion.pdf"', data)

    def test_no_oculta_unico_idioma_disponible(self):
        with workspace_fixture() as name:
            root = Path(name); shutil.copyfile(ROOT/'index.php', root/'index.php')
            (root/'FR_52_01_ConceptosVcomputacion.pdf').write_bytes(b'%PDF')
            data, _ = self.render(root, idioma='it')
            self.assertEqual(data.count(b'<article class="card">'), 1)

    def test_vacio_diagnosticado(self):
        with workspace_fixture() as name:
            root = Path(name); shutil.copyfile(ROOT/'index.php', root/'index.php')
            data, _ = self.render(root, idioma='es')
            self.assertIn(b'No hay documentos PDF disponibles', data)

    def test_rechaza_recorridos_y_no_catalogados(self):
        for filename, expected in (('../secreto.pdf', '400'), ('inexistente.pdf', '404'),
                                   ('publica.py', '400')):
            _, status = self.render(ROOT, doc=filename)
            self.assertIn('HTTP_STATUS='+expected, status)

    def test_publicador_incluye_indice(self):
        import importlib.util
        spec = importlib.util.spec_from_file_location('catalogo_publicacion', ROOT/'publicacion_remota.py')
        import sys
        sys.path.insert(0, str(ROOT))
        module = importlib.util.module_from_spec(spec)
        spec.loader.exec_module(module)
        files = module.web_files(ROOT, [])
        self.assertEqual(files['index.php'], ROOT/'web-base/index.php')

    def test_estructura_publicada_igual_que_uniholog(self):
        import sys
        sys.path.insert(0, str(ROOT))
        from publicacion_remota import web_files, publish_web
        with workspace_fixture() as name:
            root = Path(name); self.fixture(root)
            stem = 'ES_52_01_ConceptosVcomputacion'
            source = root/(stem+'.md')
            pdf = root/'pdfs'/(stem+'.pdf')
            pdf.write_bytes(b'%PDF-1.4\ncontenido de prueba')
            (root/'htmls').mkdir()
            (root/'htmls'/(stem+'.html')).write_text('<p>Prueba</p>', encoding='utf-8')
            files = web_files(root, [source])
            self.assertEqual(files['md/'+source.name], source)
            self.assertEqual(files[pdf.name], pdf)
            self.assertNotIn(source.name, files)
            self.assertNotIn('pdfs/'+pdf.name, files)
            planned = publish_web(root, [source], dry_run=True)
            self.assertIn('md/'+source.name, planned['archivos'])
            self.assertIn(pdf.name, planned['archivos'])
            self.assertEqual(planned['borrados'], 0)
            stage = root/'web_publicada'
            stage.mkdir()
            for relative, path in files.items():
                target = stage/relative
                target.parent.mkdir(parents=True, exist_ok=True)
                shutil.copyfile(path, target)
            data, _ = self.render(stage, idioma='es')
            self.assertEqual(data.count(b'<article class="card">'), 1)
            self.assertIn(('href="'+pdf.name+'"').encode(), data)
            data, _ = self.render(stage, idioma='es', doc=pdf.name)
            self.assertIn(b'btnPlay', data)
            self.assertIn('Primera ficha', data.decode())

    def test_raiz_publicada_prevalece_sobre_pdfs_antiguos(self):
        with workspace_fixture() as name:
            root = Path(name); self.fixture(root)
            (root/'ES_52_03_ConceptosVcomputacion.pdf').write_bytes(b'%PDF-root')
            data, _ = self.render(root, idioma='es')
            self.assertEqual(data.count(b'<article class="card">'), 1)
            self.assertIn(b'href="ES_52_03_ConceptosVcomputacion.pdf"', data)
            self.assertNotIn(b'pdfs/ES_52_01', data)

    def test_enlaces_web_de_pdf_sin_subdirectorio_local(self):
        import sys
        sys.path.insert(0, str(ROOT))
        from recursos_publicacion import web_url
        with patch.dict('os.environ', {'PUBLICATION_BASE_URL': 'https://site.test/teorias/pdf'}):
            self.assertEqual(web_url(ROOT/'pdfs/ES_52_total.pdf', ROOT),
                             'https://site.test/teorias/pdf/V-COMPUTACION/ES_52_total.pdf')
            self.assertEqual(web_url(ROOT.parent/'UNIHOLOG/pdfs/ES_52_Maquina_V_UNIHOLOG.pdf', ROOT),
                             'https://site.test/teorias/pdf/UNIHOLOG/ES_52_Maquina_V_UNIHOLOG.pdf')
            self.assertEqual(web_url(ROOT/'datos/ejemplo.pdf', ROOT),
                             'https://site.test/teorias/pdf/V-COMPUTACION/datos/ejemplo.pdf')

    def test_subdirectorios_encima_de_documentos(self):
        data, _ = self.render(ROOT, idioma='es')
        self.assertLess(data.index(b'class="project-subdirectories"'), data.index(b'<article class="card">'))
        for name in ('datos', 'img', 'htmls'):
            self.assertIn(('?carpeta='+name+'&amp;idioma=es').encode(), data)
        self.assertNotIn(b'?carpeta=web-base', data)

    def test_explorar_subdirectorios_y_archivos(self):
        with workspace_fixture() as name:
            root = Path(name); self.fixture(root)
            (root/'datos').mkdir()
            (root/'datos/prueba con espacios.txt').write_text('prueba', encoding='utf-8')
            (root/'datos/otro nivel').mkdir()
            data, _ = self.render(root, idioma='it', carpeta='datos')
            self.assertIn(b'datos/prueba%20con%20espacios.txt', data)
            self.assertIn(b'?carpeta=datos%2Fotro%20nivel&amp;idioma=it', data)
            self.assertIn(b'Torna al catalogo', data)
            data, _ = self.render(root, idioma='es', carpeta='datos/otro nivel')
            self.assertIn(b'No hay archivos', data)

    def test_subdirectorio_con_indice_propio(self):
        with workspace_fixture() as name:
            root = Path(name); self.fixture(root)
            (root/'json').mkdir()
            (root/'json/index.php').write_text('<?php echo "indice propio";', encoding='utf-8')
            data, _ = self.render(root, idioma='fr')
            self.assertIn(b'href="json/?idioma=fr"', data)

    def test_rechaza_directorios_ajenos_y_ocultos(self):
        for relative in ('../', '.git', 'web-base', 'datos/../../', 'datos\\..', ['datos']):
            _, status = self.render(ROOT, carpeta=relative)
            self.assertIn('HTTP_STATUS=404', status)

    def test_web_base_prevalece_y_publica_recursos(self):
        import sys
        sys.path.insert(0, str(ROOT))
        from publicacion_remota import web_files
        with workspace_fixture() as name:
            root = Path(name)
            (root/'index.php').write_text('indice obsoleto', encoding='utf-8')
            (root/'web-base/assets').mkdir(parents=True)
            source = root/'web-base/index.php'
            source.write_text('<?php echo "indice nuevo";', encoding='utf-8')
            (root/'web-base/assets/catalogo.css').write_text('body{}', encoding='utf-8')
            (root/'web-base/index.php.bak').write_text('respaldo', encoding='utf-8')
            files = web_files(root, [])
            self.assertEqual(files['index.php'], source)
            self.assertEqual(files['assets/catalogo.css'], root/'web-base/assets/catalogo.css')
            self.assertNotIn('index.php.bak', files)

    def test_plantillas_web_base_de_cada_proyecto(self):
        for project in ('UNIHOLOG', 'Einstein-VED', 'DEMUESTRA', 'V-COMPUTACION'):
            template = ROOT.parent/project/'web-base/index.php'
            with self.subTest(project=project), workspace_fixture() as name:
                root = Path(name)
                shutil.copyfile(template, root/'index.php')
                (root/'md').mkdir()
                (root/'md/prueba.md').write_text('# Documento', encoding='utf-8')
                data, status = self.render(root, idioma='es', carpeta='md')
                self.assertIn(b'href="md/prueba.md"', data)
                self.assertIn('HTTP_STATUS=200', status)


if __name__ == '__main__':
    unittest.main(verbosity=2)
