import unittest
from unittest.mock import patch
import json
from flask import Flask
from flask_testing import TestCase
from database import db_management as mldb
from machinelearning import ml as ml
from app import app, mldb, ml


class AppTestCase(TestCase):
    def create_app(self):
        app.config["TESTING"] = True
        return app

    def setUp(self):
        mldb.add_job("run1", 0.8, 0.9, "", "context1", 1)
        mldb.add_job("run2", 0.7, 0.85, "", "context2", 1)

    def tearDown(self):
        mldb.clear_jobs()

    def test_prepare_return_data(self):
        data = {"result": "test"}
        result = self.client.post("/prepare_return_data", json=data)
        self.assert200(result)
        self.assertEqual(result.get_json(), data)

    def test_getall(self):
        result = self.client.get("/getall")
        self.assert200(result)
        expected_result = [
            ("run1", 0.8, 0.9, "", "context1", 1),
            ("run2", 0.7, 0.85, "", "context2", 1),
        ]
        self.assertEqual(result.get_json(), expected_result)

    def test_get(self):
        data = {"JobId": 1}
        result = self.client.get("/get", json=data)
        self.assert200(result)
        expected_result = ("run1", 0.8, 0.9, "", "context1", 1)
        self.assertEqual(result.get_json(), expected_result)

    def test_add(self):
        data = {
            "RunId": "run3",
            "AUC": 0.75,
            "Accuracy": 0.95,
            "Arguments": "",
            "TrainingContext": "context3",
            "RunStatus": 1,
        }
        result = self.client.post("/add", json=data)
        self.assert200(result)
        self.assertEqual(result.get_json(), 3)

    def test_update(self):
        data = {
            "JobId": 1,
            "RunId": "run1_updated",
            "AUC": 0.85,
            "Accuracy": 0.92,
            "Arguments": "",
            "TrainingContext": "context1_updated",
            "RunStatus": 2,
        }
        result = self.client.put("/update", json=data)
        self.assert200(result)
        expected_result = ("run1_updated", 0.85, 0.92, "", "context1_updated", 2)
        self.assertEqual(result.get_json(), expected_result)

    @patch("machinelearning.ml.create_run_experiment")
    def test_run_exp(self, mock_create_run_experiment):
        data = {"p1": 1.0, "p2": 2.0, "p3": 3.0, "p4": 4.0}
        mock_create_run_experiment.return_value = 4
        result = self.client.post("/run", json=data)
        self.assert200(result)
        self.assertEqual(result.get_json(), 4)


class MLTestCase(unittest.TestCase):
    def test_create_run_experiment(self):
        p1 = 1.0
        p2 = 2.0
        p3 = 3.0
        p4 = 4.0
        result = ml.create_run_experiment(p1, p2, p3, p4)
        self.assertEqual(result, 1)  # Replace with the actual expected job_id


if __name__ == "__main__":
    unittest.main()
class AppTestCase(TestCase):
    def create_app(self):
        app.config["TESTING"] = True
        app.config["DEBUG"] = False
        return app

    def setUp(self):
        # Set up a test database
        self.connection = sqlite3.connect(':memory:')
        self.connection.row_factory = sqlite3.Row
        with open('database/schema.sql') as f:
            self.connection.executescript(f.read())
        self.connection.commit()

    def tearDown(self):
        self.connection.close()

    def test_prepare_return_data(self):
        result = prepare_return_data("Test Result")
        expected_result = '{"result": "Test Result"}'
        self.assertEqual(result, expected_result)

    def test_get_all(self):
        # Mock the database connection and execute a sample query
        with patch('app.get_db_connection') as mock_get_db_connection:
            mock_conn = mock_get_db_connection.return_value
            mock_conn.execute.return_value.fetchall.return_value = [('1', '2', '3')]
            result = mldb.get_all()
            self.assertEqual(result, [('1', '2', '3')])

    def test_get_job(self):
        # Mock the database connection and execute a sample query
        with patch('app.get_db_connection') as mock_get_db_connection:
            mock_conn = mock_get_db_connection.return_value
            mock_conn.execute.return_value.fetchone.return_value = ('1', '2', '3')
            result = mldb.get_job(1)
            self.assertEqual(result, ('1', '2', '3'))

    def test_add_job(self):
        # Mock the database connection and execute a sample insert statement
        with patch('app.get_db_connection') as mock_get_db_connection:
            mock_conn = mock_get_db_connection.return_value
            mock_cursor = mock_conn.cursor.return_value
            mock_cursor.lastrowid = 1
            result = mldb.add_job('Run123', 0.9, 0.8, '["arg1", "arg2"]', 'Context', 1)
            self.assertEqual(result, 1)
            mock_cursor.execute.assert_called_once_with(
                "INSERT INTO Jobs (RunId, AUC, Accuracy, Arguments, TrainingContext, RunStatus) VALUES (?, ?, ?, ?, ?, ?)",
                ('Run123', 0.9, 0.8, '["arg1", "arg2"]', 'Context', 1)
            )

    def test_update_job(self):
        # Mock the database connection and execute a sample update statement
        with patch('app.get_db_connection') as mock_get_db_connection:
            mock_conn = mock_get_db_connection.return_value
            mock_cursor = mock_conn.cursor.return_value
            mock_cursor.execute.return_value = ('1', 'Run123', 0.9, 0.8, '["arg1", "arg2"]', 'Context', 1)
            result = mldb.update_job(1, 'Run123', 0.9, 0.8, '["arg1", "arg2"]', 'Context', 1)
            self.assertEqual(result, ('1', 'Run123', 0.9, 0.8, '["arg1", "arg2"]', 'Context', 1))
            mock_cursor.execute.assert_called_once_with(
                "UPDATE Jobs SET RunId = ?, AUC = ?, Accuracy = ?, Arguments = ?, TrainingContext = ?, RunStatus = ? WHERE Id = ?",
                ('Run123', 0.9,   0.8, '["arg1", "arg2"]', 'Context', 1, 1)
            )

    def test_delete_job(self):
        # Mock the database connection and execute a sample delete statement
        with patch('app.get_db_connection') as mock_get_db_connection:
            mock_conn = mock_get_db_connection.return_value
            mock_cursor = mock_conn.cursor.return_value
            result = mldb.delete_job(1)
            self.assertEqual(result, None)
            mock_cursor.execute.assert_called_once_with(
                "DELETE FROM Jobs WHERE Id = ?",
                (1,)
            )

    def test_train_model(self):
        # Mock the machine learning model and execute a training process
        with patch('app.ml.train') as mock_train:
            mock_train.return_value = {'accuracy': 0.9, 'auc': 0.8}
            result = ml.train('dataset.csv')
            self.assertEqual(result, {'accuracy': 0.9, 'auc': 0.8})

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