import copy
import http.client
import json
from pathlib import Path
import tempfile
import threading
import unittest
from server import build_server, ROOT


class LocalServerTests(unittest.TestCase):
    def setUp(self):
        self.temp = tempfile.TemporaryDirectory()
        config = json.loads((ROOT / 'app_config.json').read_text())
        config['data_directory'] = self.temp.name
        self.token = 'synthetic-test-token-' + 'x' * 32
        self.server = build_server(config, self.token, 0)
        self.thread = threading.Thread(target=self.server.serve_forever, daemon=True)
        self.thread.start()

    def tearDown(self):
        self.server.shutdown()
        self.server.server_close()
        self.thread.join()
        self.temp.cleanup()

    def call(self, path, method='GET', payload=None, auth=True, headers=None):
        connection = http.client.HTTPConnection('127.0.0.1', self.server.server_port, timeout=5)
        hs = {'Authorization': 'Bearer ' + self.token} if auth else {}
        if payload is not None:
            hs['Content-Type'] = 'application/json'
        hs.update(headers or {})
        body = json.dumps(payload) if payload is not None else None
        connection.request(method, path, body=body, headers=hs)
        response = connection.getresponse()
        data = response.read()
        result = (response.status, dict(response.getheaders()), data)
        connection.close()
        return result

    def event(self, identity='local-test'):
        return {'id': identity, 'bot': 'mark', 'category': 'knowledge',
                'source': {'title': 'Synthetic validation fixture'},
                'startedAt': '2026-10-10T20:00:00Z', 'endedAt': '2026-10-10T20:05:00Z',
                'summary': 'Test only', 'importance': 'Test', 'improvement': 'Untested'}

    def test_login_and_authentication(self):
        self.assertEqual(self.call('/login', auth=False)[0], 200)
        self.assertEqual(self.call('/api/snapshot', auth=False)[0], 401)
        self.assertEqual(self.call('/api/events', 'POST', [self.event()], auth=False)[0], 401)
        connection = http.client.HTTPConnection('127.0.0.1', self.server.server_port)
        connection.request('POST', '/login', body='token=' + self.token,
                           headers={'Content-Type': 'application/x-www-form-urlencoded'})
        response = connection.getresponse()
        self.assertEqual(response.status, 303)
        cookie = response.getheader('Set-Cookie'); response.read(); connection.close()
        self.assertIn('HttpOnly', cookie)
        self.assertEqual(self.call('/', auth=False, headers={'Cookie': cookie.split(';')[0]})[0], 200)

    def test_dashboard_and_static_routes(self):
        for path in ['/', '/manifest.webmanifest', '/icon.svg']:
            self.assertEqual(self.call(path)[0], 200)
        self.assertEqual(self.call('/not-found')[0], 404)
        self.assertEqual(self.call('/app_config.json')[0], 404)
        self.assertEqual(self.call('/data/local-access-token.txt')[0], 404)

    def test_snapshot_profiles_without_training_claim(self):
        status, _, raw = self.call('/api/snapshot')
        body = json.loads(raw)
        self.assertEqual(status, 200)
        self.assertEqual(len(body['bots']), 8)
        self.assertFalse(body['trainerConnected'])
        self.assertEqual(body['events'], [])

    def test_import_readback_duplicate_and_batch_atomicity(self):
        self.assertEqual(self.call('/api/events', 'POST', [self.event()])[0], 200)
        self.assertEqual(self.call('/api/events', 'POST', [self.event()])[0], 409)
        self.assertEqual(self.call('/api/events', 'POST', [self.event('new-id'), self.event()])[0], 409)
        data = json.loads(self.call('/api/snapshot')[2])
        self.assertEqual(len(data['events']), 1)
        self.assertEqual(data['events'][0]['verification'], 'Reported')

    def test_invalid_event_rejected(self):
        event = self.event(); event['bot'] = 'unknown'
        self.assertEqual(self.call('/api/events', 'POST', [event])[0], 400)

    def test_foreign_origin_and_host_rejected(self):
        self.assertEqual(self.call('/api/events', 'POST', [self.event()], headers={'Origin': 'https://evil.test'})[0], 403)
        self.assertEqual(self.call('/', headers={'Host': 'evil.test'})[0], 403)

    def test_oversized_body_rejected(self):
        self.assertEqual(self.call('/api/events', 'POST', ['x' * 200001])[0], 413)

    def test_bad_cursor_rejected(self):
        self.assertEqual(self.call('/api/snapshot?cursor=-1')[0], 400)


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