import datetime
import os
import subprocess
import base64
from pathlib import Path
import shutil
import pandas as pd
import signal
import requests

from baselayer.app.env import load_env
from baselayer.app.model_util import status, create_tables, drop_tables
from social_tornado.models import TornadoStorage
from skyportal.models import init_db, Base, DBSession, Source, User
from skyportal.model_util import setup_permissions, create_token
from skyportal.tests import api
from baselayer.tools.test_frontend import verify_server_availability


if __name__ == "__main__":
    """Insert test data"""
    env, cfg = load_env()
    basedir = Path(os.path.dirname(__file__)) / ".."

    with status(f"Connecting to database {cfg['database']['database']}"):
        init_db(**cfg["database"])

    with status("Dropping all tables"):
        drop_tables()

    with status("Creating tables"):
        create_tables()

    for model in Base.metadata.tables:
        print("    -", model)

    with status(f"Creating permissions"):
        setup_permissions()

    with status(f"Creating dummy users"):
        super_admin_user = User(
            username="testuser@cesium-ml.org", role_ids=["Super admin"]
        )
        group_admin_user = User(
            username="groupadmin@cesium-ml.org", role_ids=["Super admin"]
        )
        full_user = User(username="fulluser@cesium-ml.org", role_ids=["Full user"])
        view_only_user = User(
            username="viewonlyuser@cesium-ml.org", role_ids=["View only"]
        )
        DBSession().add_all(
            [super_admin_user, group_admin_user, full_user, view_only_user]
        )

        for u in [super_admin_user, group_admin_user, full_user, view_only_user]:
            DBSession().add(
                TornadoStorage.user.create_social_auth(u, u.username, "google-oauth2")
            )

    with status("Creating token"):
        token = create_token(
            [
                "Manage groups",
                "Manage sources",
                "Upload data",
                "Comment",
                "Manage users",
            ],
            super_admin_user.id,
            "load_demo_data token",
        )

    def assert_post(endpoint, data):
        response_status, data = api("POST", endpoint, data, token)
        if not response_status == 200 and data["status"] == "success":
            raise RuntimeError(
                f'API call to {endpoint} failed with status {status}: {data["message"]}'
            )
        return data

    with status("Launching web app & executing API calls"):
        try:
            response_status, data = api("GET", "sysinfo", token=token)
            app_already_running = True
        except requests.ConnectionError:
            app_already_running = False
            web_client = subprocess.Popen(
                ["make", "run"], cwd=basedir, preexec_fn=os.setsid
            )

        server_url = f"http://localhost:{cfg['ports.app']}"
        print()
        print(f"Waiting for server to appear at {server_url}...")

        try:
            verify_server_availability(server_url)
            print("App running - continuing with API calls")

            with status("Creating dummy group & adding users"):
                data = assert_post(
                    "groups",
                    data={
                        "name": "Stream A",
                        "group_admins": [
                            super_admin_user.username,
                            group_admin_user.username,
                        ],
                    },
                )
                group_id = data["data"]["id"]

                for u in [view_only_user, full_user]:
                    data = assert_post(
                        f"groups/{group_id}/users/{u.username}", data={"admin": False}
                    )

            with status("Creating dummy instruments"):
                data = assert_post(
                    "telescope",
                    data={
                        "name": "Palomar 1.5m",
                        "nickname": "P60",
                        "lat": 33.3633675,
                        "lon": -116.8361345,
                        "elevation": 1870,
                        "diameter": 1.5,
                        "group_ids": [group_id],
                    },
                )
                telescope1_id = data["data"]["id"]

                data = assert_post(
                    "instrument",
                    data={
                        "name": "P60 Camera",
                        "type": "phot",
                        "band": "optical",
                        "telescope_id": telescope1_id,
                    },
                )
                instrument1_id = data["data"]["id"]

                data = assert_post(
                    "telescope",
                    data={
                        "name": "Nordic Optical Telescope",
                        "nickname": "NOT",
                        "lat": 28.75,
                        "lon": 17.88,
                        "elevation": 1870,
                        "diameter": 2.56,
                        "group_ids": [group_id],
                    },
                )
                telescope2_id = data["data"]["id"]

                data = assert_post(
                    "instrument",
                    data={
                        "name": "ALFOSC",
                        "type": "both",
                        "band": "optical",
                        "telescope_id": telescope2_id,
                    },
                )

            with status("Creating dummy sources"):
                SOURCES = [
                    {
                        "id": "14gqr",
                        "ra": 353.36647,
                        "dec": 33.646149,
                        "redshift": 0.063,
                        "group_ids": [group_id],
                        "comments": [
                            "No source at transient location to R>26 in LRIS imaging",
                            "Strong calcium lines have emerged.",
                        ],
                    },
                    {
                        "id": "16fil",
                        "ra": 322.718872,
                        "dec": 27.574113,
                        "redshift": 0.0,
                        "group_ids": [group_id],
                        "comments": ["Frogs in the pond", "The eagle has landed"],
                    },
                ]

                (basedir / "static/thumbnails").mkdir(parents=True, exist_ok=True)
                for source_info in SOURCES:
                    comments = source_info.pop("comments")

                    data = assert_post("sources", data=source_info)
                    assert data["data"]["id"] == source_info["id"]

                    for comment in comments:
                        data = assert_post(
                            "comment",
                            data={"source_id": source_info["id"], "text": comment},
                        )

                    phot_file = basedir / "skyportal/tests/data/phot.csv"
                    phot_data = pd.read_csv(phot_file)

                    data = assert_post(
                        "photometry",
                        data={
                            "source_id": source_info["id"],
                            "time_format": "iso",
                            "time_scale": "utc",
                            "instrument_id": instrument1_id,
                            "observed_at": phot_data.observed_at.tolist(),
                            "mag": phot_data.mag.tolist(),
                            "e_mag": phot_data.e_mag.tolist(),
                            "lim_mag": phot_data.lim_mag.tolist(),
                            "filter": phot_data["filter"].tolist(),
                        },
                    )

                    spec_file = os.path.join(
                        os.path.dirname(os.path.dirname(__file__)),
                        "skyportal",
                        "tests",
                        "data",
                        "spec.csv",
                    )
                    spec_data = pd.read_csv(spec_file)
                    for i, df in spec_data.groupby("instrument_id"):
                        data = assert_post(
                            "spectrum",
                            data={
                                "source_id": source_info["id"],
                                "observed_at": str(datetime.datetime(2014, 10, 24)),
                                "instrument_id": 1,
                                "wavelengths": df.wavelength.tolist(),
                                "fluxes": df.flux.tolist(),
                            },
                        )

                    for ttype in ["new", "ref", "sub"]:
                        fname = f'{source_info["id"]}_{ttype}.png'
                        fpath = basedir / f"skyportal/tests/data/{fname}"
                        thumbnail_data = base64.b64encode(
                            open(os.path.abspath(fpath), "rb").read()
                        )
                        data = assert_post(
                            "thumbnail",
                            data={
                                "source_id": source_info["id"],
                                "data": thumbnail_data,
                                "ttype": ttype,
                            },
                        )

                    source = Source.query.get(source_info["id"])
                    source.add_linked_thumbnails()
        finally:
            if not app_already_running:
                print("Terminating web app")
                os.killpg(os.getpgid(web_client.pid), signal.SIGTERM)

[(22, 'baselayer.app.env.load_env', 'load_env', False, 'from baselayer.app.env import load_env\n'), (25, 'baselayer.app.model_util.status', 'status', False, 'from baselayer.app.model_util import status, create_tables, drop_tables\n'), (26, 'skyportal.models.init_db', 'init_db', False, 'from skyportal.models import init_db, Base, DBSession, Source, User\n'), (28, 'baselayer.app.model_util.status', 'status', False, 'from baselayer.app.model_util import status, create_tables, drop_tables\n'), (29, 'baselayer.app.model_util.drop_tables', 'drop_tables', False, 'from baselayer.app.model_util import status, create_tables, drop_tables\n'), (31, 'baselayer.app.model_util.status', 'status', False, 'from baselayer.app.model_util import status, create_tables, drop_tables\n'), (32, 'baselayer.app.model_util.create_tables', 'create_tables', False, 'from baselayer.app.model_util import status, create_tables, drop_tables\n'), (37, 'baselayer.app.model_util.status', 'status', False, 'from baselayer.app.model_util import status, create_tables, drop_tables\n'), (38, 'skyportal.model_util.setup_permissions', 'setup_permissions', False, 'from skyportal.model_util import setup_permissions, create_token\n'), (40, 'baselayer.app.model_util.status', 'status', False, 'from baselayer.app.model_util import status, create_tables, drop_tables\n'), (41, 'skyportal.models.User', 'User', False, 'from skyportal.models import init_db, Base, DBSession, Source, User\n'), (44, 'skyportal.models.User', 'User', False, 'from skyportal.models import init_db, Base, DBSession, Source, User\n'), (47, 'skyportal.models.User', 'User', False, 'from skyportal.models import init_db, Base, DBSession, Source, User\n'), (48, 'skyportal.models.User', 'User', False, 'from skyportal.models import init_db, Base, DBSession, Source, User\n'), (60, 'baselayer.app.model_util.status', 'status', False, 'from baselayer.app.model_util import status, create_tables, drop_tables\n'), (61, 'skyportal.model_util.create_token', 'create_token', False, 'from skyportal.model_util import setup_permissions, create_token\n'), (74, 'skyportal.tests.api', 'api', False, 'from skyportal.tests import api\n'), (81, 'baselayer.app.model_util.status', 'status', False, 'from baselayer.app.model_util import status, create_tables, drop_tables\n'), (23, 'os.path.dirname', 'os.path.dirname', False, 'import os\n'), (83, 'skyportal.tests.api', 'api', False, 'from skyportal.tests import api\n'), (96, 'baselayer.tools.test_frontend.verify_server_availability', 'verify_server_availability', False, 'from baselayer.tools.test_frontend import verify_server_availability\n'), (51, 'skyportal.models.DBSession', 'DBSession', False, 'from skyportal.models import init_db, Base, DBSession, Source, User\n'), (57, 'social_tornado.models.TornadoStorage.user.create_social_auth', 'TornadoStorage.user.create_social_auth', False, 'from social_tornado.models import TornadoStorage\n'), (87, 'subprocess.Popen', 'subprocess.Popen', False, 'import subprocess\n'), (99, 'baselayer.app.model_util.status', 'status', False, 'from baselayer.app.model_util import status, create_tables, drop_tables\n'), (117, 'baselayer.app.model_util.status', 'status', False, 'from baselayer.app.model_util import status, create_tables, drop_tables\n'), (167, 'baselayer.app.model_util.status', 'status', False, 'from baselayer.app.model_util import status, create_tables, drop_tables\n'), (56, 'skyportal.models.DBSession', 'DBSession', False, 'from skyportal.models import init_db, Base, DBSession, Source, User\n'), (204, 'pandas.read_csv', 'pd.read_csv', True, 'import pandas as pd\n'), (228, 'pandas.read_csv', 'pd.read_csv', True, 'import pandas as pd\n'), (256, 'skyportal.models.Source.query.get', 'Source.query.get', False, 'from skyportal.models import init_db, Base, DBSession, Source, User\n'), (261, 'os.getpgid', 'os.getpgid', False, 'import os\n'), (222, 'os.path.dirname', 'os.path.dirname', False, 'import os\n'), (234, 'datetime.datetime', 'datetime.datetime', False, 'import datetime\n'), (245, 'os.path.abspath', 'os.path.abspath', False, 'import os\n')]