from datetime import datetime

import pytest
from sqlalchemy import text

from xplordb.datamodel.collar import Collar
from xplordb.datamodel.metadata import RawCollarMetadata
from xplordb.datamodel.company import Company
from xplordb.datamodel.dataset import Dataset
from xplordb.datamodel.person import Person
from xplordb.datamodel.survey import Survey
from xplordb.import_data import ImportData
from tests.conftest import COMPANY, PERSON, DATASET, COMPANY_B, COMPANY_B_PERSON, COMPANY_B_DATASET



def test_metadata_import(data_importer_with_dataset, db_session):

    # get dataset ID
    dataset_id = data_importer_with_dataset._get_dataset_id(DATASET)
    # add some collars
    collars = [Collar('collar', DATASET, PERSON, 100.0, 100.0, 0.0, 3857, 100.0, 100.0, 0.0,1000),
               Collar('collar2', DATASET, PERSON, 100.0, 100.0, 0.0, 3857, 100.0, 100.0, 0.0,None),
               Collar('collar3', DATASET, PERSON, 100.0, 100.0, 0.0, 3857, 100.0, 100.0, 0.0,None, None)]
    data_importer_with_dataset.import_collars_array(collars)
    data_importer_with_dataset.commit()

    # import metadata
    metadatas = [RawCollarMetadata(hole_id="collar", data_set=DATASET, extra_cols={"col_a" : 10}),
                 RawCollarMetadata(hole_id="collar3", data_set=DATASET, extra_cols={"col_b" : 5})]
    
    data_importer_with_dataset.import_metadatas_array(metadatas)
    data_importer_with_dataset.commit()
    expected = [(dataset_id, "collar", 10, None), 
                (dataset_id, "collar2", None, None), 
                (dataset_id, "collar3", None, 5)]
    
    with db_session.cursor() as cur:
        cur.execute("SELECT * FROM dh.metadata ORDER BY hole_id")
        values = cur.fetchall()
        assert len(values) == len(collars)
        for value, exp in zip(values, expected):
            assert value == exp

def test_duplicated_company_import(data_importer, db_session):
    """
    Test duplicated company import
    """
    companies = [Company("company_a"), Company("company_b"), Company("company_b")]

    with pytest.raises(ImportData.ImportException):
        data_importer.import_companies_array(companies)
        data_importer.commit()


def test_duplicated_person_import(data_importer, db_session):
    """
    Test duplicated person import
    """
    company = Company("company_a")
    data_importer.import_companies_array([company])

    persons = [Person('a', company),
               Person('b', company),
               Person('b', company) ]
    with pytest.raises(ImportData.ImportException):
        data_importer.import_persons_array(persons)
        data_importer.commit()


def test_person_import(data_importer, db_session):
    """
    Test person import
    """
    company = Company("company_a")
    data_importer.import_companies_array([company])

    persons = [Person('a', company),
               Person('b', company),
               Person('c', company) ]
    data_importer.import_persons_array(persons)
    data_importer.commit()

    with db_session.cursor() as cur:
        cur.execute("SELECT id, name, company FROM ref.person")
        values = cur.fetchall()
        assert len(values) == len(persons)
        for i in range(0, len(values)):
            assert values[i][0] == i + 1
            assert values[i][1] == persons[i].name
            assert values[i][2] == persons[i].company.name


def test_get_available_persons(data_importer, db_session):
    """
    Test person read from database
    """
    test_person_import(data_importer, db_session)
    persons = data_importer.get_available_persons()

    assert [person.name for person in persons] == ["a", "b", "c"]
    assert [person.company.name for person in persons] == ["company_a", "company_a", "company_a"]


def test_dataset_import_invalid_person(data_importer_with_dataset):
    """
    Test dataset import with invalid person
    """

    with pytest.raises(ImportData.ImportException):
        datasets = [Dataset('test', Person("invalid", COMPANY))]
        data_importer_with_dataset.import_datasets_array(datasets)
        data_importer_with_dataset.commit()


def test_dataset_import(data_importer, db_session):
    """
    Test dataset import
    """
    company = Company("company_a")
    data_importer.import_companies_array([company])

    person = Person('xdb', company)
    data_importer.import_persons_array([person])

    datasets = [Dataset('test', person),
                Dataset('test2', person), ]
    
    data_importer.import_datasets_array(datasets)
    data_importer.commit()

    person_id = data_importer._get_person_id(PERSON)
    with db_session.cursor() as cur:
        cur.execute("SELECT id, name, person FROM ref.data_sets")
        values = cur.fetchall()
        assert len(values) == len(datasets)
        for i in range(0, len(values)):
            assert values[i][0] == i + 1
            assert values[i][1] == datasets[i].name
            assert values[i][2] == person_id

def test_duplicated_dataset_import(data_importer, db_session):
    """
    Test dataset import
    """
    company = Company("company_a")
    data_importer.import_companies_array([company])

    person = Person('xdb', company)
    data_importer.import_persons_array([person])

    datasets = [Dataset('test', person),
                Dataset('test', person), ]

    with pytest.raises(ImportData.ImportException):
        data_importer.import_datasets_array(datasets)
        data_importer.commit()



def test_get_available_datasets(data_importer, db_session):
    """
    Test datasets read from database
    """
    test_dataset_import(data_importer, db_session)
    datasets = data_importer.get_available_datasets()
    assert [dataset.name for dataset in datasets] == ['test', 'test2']
    assert [dataset.person.name for dataset in datasets] == ['xdb', 'xdb']
    assert [dataset.person.company.name for dataset in datasets] == ['company_a', 'company_a']


def test_collar_import_invalid_fk(data_importer_with_dataset):
    """
    Test collar import with an invalid foreign key
    """

    with pytest.raises(ImportData.ImportException):
        collar = [Collar('collar',Dataset('test2', PERSON), PERSON, 100.0, 100.0, 0.0, 3857), ]
        data_importer_with_dataset.import_collars_array(collar)
        data_importer_with_dataset.commit()


def test_collar_import(data_importer_with_dataset, db_session):
    """
    Test collar import
    """

    collars = [Collar('collar', DATASET,  PERSON, 100.0, 100.0, 0.0, 3857, 100.0, 100.0, 0.0,1000),
               Collar('collar2', DATASET,  PERSON, 100.0, 100.0, 0.0, 3857),
               Collar('collar3', DATASET,  PERSON, 100.0, 100.0, 0.0, 3857, None, None, None,None, None)]
    data_importer_with_dataset.import_collars_array(collars)
    data_importer_with_dataset.commit()

    person_id = data_importer_with_dataset._get_person_id(PERSON)
    dataset_id = data_importer_with_dataset._get_dataset_id(DATASET)

    with db_session.cursor() as cur:
        cur.execute("SELECT data_set,hole_id,loaded_by,x,y,z,srid,eoh, planned_x, planned_y, planned_z, planned_eoh FROM dh.collar")
        values = cur.fetchall()
        assert len(values) == len(collars)
        for i in range(0, len(values)):
            assert values[i][0] == dataset_id
            assert values[i][1] == collars[i].hole_id
            assert values[i][2] == person_id
            assert values[i][3] == collars[i].x
            assert values[i][4] == collars[i].y
            assert values[i][5] == collars[i].z
            assert values[i][6] == collars[i].srid
            assert values[i][7] == collars[i].eoh
            assert values[i][8] == collars[i].planned_x
            assert values[i][9] == collars[i].planned_y
            assert values[i][10] == collars[i].planned_z
            assert values[i][11] == collars[i].planned_eoh

def test_duplicated_collar_import(data_importer_with_dataset, db_session):
    """
    Test duplicated collar import
    """

    collars = [Collar('collar', DATASET,  PERSON, 100.0, 100.0, 0.0, 3857, 100.0, 100.0, 0.0,1000),
               Collar('collar2', DATASET,  PERSON, 100.0, 100.0, 0.0, 3857),
               Collar('collar2', DATASET,  PERSON, 100.0, 100.0, 0.0, 3857, None, None, None,None, None)]
    with pytest.raises(ImportData.ImportException):
        data_importer_with_dataset.import_collars_array(collars)
        data_importer_with_dataset.commit()



def test_survey_import_invalid_fk(data_importer_with_dataset):
    """
    Test survey import with an invalid foreign key
    """
    
    with pytest.raises(ImportData.ImportException):
        surveys = [Survey('collar', Dataset('test2', PERSON), PERSON, 0.0, 45.0, 0.0), ]
        data_importer_with_dataset.import_surveys_array(surveys)
        data_importer_with_dataset.commit()


def test_survey_import(data_importer_with_dataset, db_session):
    """
    Test survey import
    """

    collars = [Collar('collar', DATASET, PERSON, 100.0, 100.0, 0.0, 3857), ]
    data_importer_with_dataset.import_collars_array(collars)

    surveys = [Survey('collar', DATASET, PERSON, 0.0, 45.0, 0.0), ]
    data_importer_with_dataset.import_surveys_array(surveys)
    data_importer_with_dataset.commit()

    person_id = data_importer_with_dataset._get_person_id(PERSON)
    dataset_id = data_importer_with_dataset._get_dataset_id(DATASET)

    with db_session.cursor() as cur:
        cur.execute("SELECT data_set,hole_id,loaded_by,depth_m,dip,azimuth_grid FROM dh.surv")
        values = cur.fetchall()
        assert len(values) == len(surveys)
        for i in range(0, len(values)):
            assert values[i][0] == dataset_id
            assert values[i][1] == surveys[i].hole_id
            assert values[i][2] == person_id
            assert values[i][3] == surveys[i].depth
            assert values[i][4] == surveys[i].dip
            assert values[i][5] == surveys[i].azimuth

def test_duplicated_survey_import(data_importer_with_dataset, db_session):
    """
    Test duplicated survey import
    """

    collars = [Collar('collar', DATASET, PERSON, 100.0, 100.0, 0.0, 3857), ]
    data_importer_with_dataset.import_collars_array(collars)
    surveys = [Survey('collar', DATASET, PERSON, 0.0, 45.0, 0.0), Survey('collar', DATASET, PERSON, 2.0, 45.0, 0.0), Survey('collar', DATASET, PERSON, 2.0, 45.0, 0.0)]
    with pytest.raises(ImportData.ImportException):
        data_importer_with_dataset.import_surveys_array(surveys)
        data_importer_with_dataset.commit()

@pytest.fixture()
def test_multi_company_collar_import(data_importer_with_two_companies, db_session):
    """
    Test collar import with same hole ids.
    """
    collars = [
        # company A
        Collar('collar', DATASET,  PERSON, 100.0, 100.0, 0.0, 3857, 100.0, 100.0, 0.0,1000),
        Collar('collar2', DATASET,  PERSON, 100.0, 100.0, 0.0, 3857),
        Collar('collar3', DATASET,  PERSON, 100.0, 100.0, 0.0, 3857, None, None, None,None, None),
        # company B
        Collar('collar', COMPANY_B_DATASET,  COMPANY_B_PERSON, 100.0, 100.0, 0.0, 3857, 100.0, 100.0, 0.0,1000),
        Collar('collar2', COMPANY_B_DATASET,  COMPANY_B_PERSON, 100.0, 100.0, 0.0, 3857),
        Collar('collar3', COMPANY_B_DATASET,  COMPANY_B_PERSON, 100.0, 100.0, 0.0, 3857, None, None, None,None, None)
        ]
    data_importer_with_two_companies.import_collars_array(collars)
    data_importer_with_two_companies.commit()

    yield data_importer_with_two_companies


@pytest.fixture()
def test_multi_company_surv_import(test_multi_company_collar_import, db_session):
    """
    Test collar import with same hole ids.
    """
    surveys = [
    # company A
    Survey('collar', DATASET, PERSON, 0.0, 45.0, 0.0),
    Survey('collar', DATASET, PERSON, 2.0, 45.0, 0.0),
    # company B
    Survey('collar', COMPANY_B_DATASET, COMPANY_B_PERSON, 0.0, 45.0, 0.0),
    Survey('collar', COMPANY_B_DATASET, COMPANY_B_PERSON, 2.0, 45.0, 0.0)
    ]
    test_multi_company_collar_import.import_surveys_array(surveys)
    test_multi_company_collar_import.commit()

    yield test_multi_company_collar_import


def test_multi_company_delete_collar(test_multi_company_surv_import, db_session):
    
    dataset_id = test_multi_company_surv_import._get_dataset_id(DATASET)

    # delete collar from first company, check deleted items from cascade
    with db_session.cursor() as cur:
        cur.execute(f"DELETE FROM dh.collar WHERE data_set = {dataset_id} AND hole_id = 'collar'")
        db_session.commit()
        # 1 row removed
        cur.execute(f"select * from dh.collar")
        res = cur.fetchall()
        assert len(res) == 5

        # 2 rows removed
        cur.execute(f"select * from dh.surv")
        res = cur.fetchall()
        assert len(res) == 2

        # 1 row removed
        cur.execute(f"select * from dh.metadata")
        res = cur.fetchall()
        assert len(res) == 5

        # 1 row removed
        cur.execute(f"select * from display.display_collar")
        res = cur.fetchall()
        assert len(res) == 5


def test_multi_company_delete_dataset(test_multi_company_surv_import, db_session):
    
    dataset_id = test_multi_company_surv_import._get_dataset_id(DATASET)

    # delete collar from first company, check surveys have been deleted (cascade)
    with db_session.cursor() as cur:
        cur.execute(f"DELETE FROM ref.data_sets WHERE id = {dataset_id}")
        db_session.commit()
        # 3 row removed
        cur.execute(f"select * from dh.collar")
        res = cur.fetchall()
        assert len(res) == 3

        # 2 rows removed
        cur.execute(f"select * from dh.surv")
        res = cur.fetchall()
        assert len(res) == 2

        # 3 row removed
        cur.execute(f"select * from dh.metadata")
        res = cur.fetchall()
        assert len(res) == 3

        # 3 row removed
        cur.execute(f"select * from display.display_collar")
        res = cur.fetchall()
        assert len(res) == 3

def test_multi_company_delete_person(test_multi_company_surv_import, db_session):
    
    person_id = test_multi_company_surv_import._get_person_id(PERSON)

    # delete collar from first company, check surveys have been deleted (cascade)
    with db_session.cursor() as cur:
        cur.execute(f"DELETE FROM ref.person WHERE id = {person_id}")
        db_session.commit()
        # 3 row removed
        cur.execute(f"select * from dh.collar")
        res = cur.fetchall()
        assert len(res) == 3

        # 2 rows removed
        cur.execute(f"select * from dh.surv")
        res = cur.fetchall()
        assert len(res) == 2

        # 3 row removed
        cur.execute(f"select * from dh.metadata")
        res = cur.fetchall()
        assert len(res) == 3

        # 3 row removed
        cur.execute(f"select * from display.display_collar")
        res = cur.fetchall()
        assert len(res) == 3

def test_multi_company_delete_company(test_multi_company_surv_import, db_session):

    # delete collar from first company, check surveys have been deleted (cascade)
    with db_session.cursor() as cur:
        cur.execute(f"DELETE FROM ref.company WHERE name = '{COMPANY.name}'")
        db_session.commit()
        # 3 row removed
        cur.execute(f"select * from dh.collar")
        res = cur.fetchall()
        assert len(res) == 3

        # 2 rows removed
        cur.execute(f"select * from dh.surv")
        res = cur.fetchall()
        assert len(res) == 2

        # 3 row removed
        cur.execute(f"select * from dh.metadata")
        res = cur.fetchall()
        assert len(res) == 3

        # 3 row removed
        cur.execute(f"select * from display.display_collar")
        res = cur.fetchall()
        assert len(res) == 3
