import hashlib
import json
from pathlib import Path
import pytest
from startup_app.app import create_app
from tools.publish_materials import publish


@pytest.fixture
def materials_client(tmp_path):
    files = tmp_path / 'public' / 'files'
    files.mkdir(parents=True)
    content = [('example.py', 'code', '<script>alert("x")</script>\nprint("안녕")'),
               ('data.csv', 'csv', '이름,값\n<script>,2\n'), ('map.html', 'map', '<html>map</html>'),
               ('plot.png', 'image', 'image')]
    entries = []
    for i, (name, kind, text) in enumerate(content):
        (files / name).write_text(text, encoding='utf-8')
        entries.append({'id': str(i) * 20, 'path': name, 'kind': kind, 'size': len(text.encode()), 'sha256': 'example', 'rows': 1, 'columns': 2})
    (tmp_path / 'outside.txt').write_text('not-public', encoding='utf-8')
    entries.append({'id': 'f' * 20, 'path': '../../outside.txt', 'kind': 'text', 'size': 10, 'sha256': 'example'})
    (tmp_path / 'public' / 'manifest.json').write_text(json.dumps({'created': 'today', 'files': entries}), encoding='utf-8')
    app = create_app()
    app.config['MATERIALS_DIR'] = tmp_path / 'public'
    return app.test_client()


def test_index_search_and_no_arbitrary_directory(materials_client):
    assert 'example.py' in materials_client.get('/materials/').text
    filtered = materials_client.get('/materials/?q=example').text
    assert 'example.py' in filtered and 'plot.png' not in filtered
    assert materials_client.get('/materials/?folder=../../').status_code == 404
    assert materials_client.get('/materials/download/.env').status_code == 404
    assert materials_client.get('/materials/download/' + 'f' * 20).status_code == 404


def test_source_and_csv_are_escaped(materials_client):
    source = materials_client.get('/materials/file/' + '0' * 20)
    assert '&lt;script&gt;' in source.text and '<script>alert' not in source.text
    data = materials_client.get('/materials/file/' + '1' * 20)
    assert '&lt;script&gt;' in data.text and '<script>' not in data.text


def test_download_and_preview_isolation(materials_client):
    download = materials_client.get('/materials/download/' + '0' * 20)
    assert download.status_code == 200 and 'attachment' in download.headers['Content-Disposition']
    frame = materials_client.get('/materials/file/' + '2' * 20)
    assert 'sandbox="allow-scripts"' in frame.text
    preview = materials_client.get('/materials/preview/' + '2' * 20)
    assert preview.status_code == 200 and 'sandbox allow-scripts;' in preview.headers['Content-Security-Policy']
    assert 'allow-same-origin' not in preview.headers['Content-Security-Policy']
    assert materials_client.get('/materials/preview/' + '0' * 20).status_code == 404
    assert materials_client.get('/materials/image/' + '0' * 20).status_code == 404
    assert 'frame-src' in frame.headers['Content-Security-Policy']


def test_export_excludes_private_folders_and_stops_on_secret(tmp_path):
    root, output = tmp_path / 'project', tmp_path / 'public'
    for folder in ('data', 'output', 'cc/startup_app', 'cc/tests', 'cc/tools', 'cc/.venv'):
        (root / folder).mkdir(parents=True)
    (root / 'app.py').write_text('print("hello")', encoding='utf-8')
    (root / 'cc' / '.venv' / 'private.py').write_text('private', encoding='utf-8')
    (root / '.env').write_text('private', encoding='utf-8')
    for name in ('requirements.txt', 'requirements-dev.txt'):
        (root / 'cc' / name).write_text('package', encoding='utf-8')
    publish(root, output)
    manifest = json.loads((output / 'manifest.json').read_text(encoding='utf-8'))
    assert len(manifest['files']) == 3
    assert all('.env' not in f['path'] and '.venv' not in f['path'] for f in manifest['files'])
    for entry in manifest['files']:
        assert hashlib.sha256((output / 'files' / entry['path']).read_bytes()).hexdigest() == entry['sha256']
    (root / 'app.py').write_text('token = "' + 'sk-' + 'x' * 40 + '"', encoding='utf-8')
    with pytest.raises(ValueError, match='공개 중단'):
        publish(root, output)
