# -*- encoding: utf-8 -*-
#
# Copyright 2015-2016 Red Hat, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
#    http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.

import dci.app
from dci import dci_config
from dci.db import models
import tests.utils as utils
import tests.sso_tokens as sso_tokens

from passlib.apps import custom_app_context as pwd_context
import contextlib
import pytest
import sqlalchemy_utils.functions
from sqlalchemy.orm import sessionmaker

import uuid


@pytest.fixture(scope="session")
def engine(request):
    utils.rm_upload_folder()
    db_uri = utils.conf["SQLALCHEMY_DATABASE_URI"]

    engine = dci_config.get_engine()

    if not sqlalchemy_utils.functions.database_exists(db_uri):
        sqlalchemy_utils.functions.create_database(db_uri)
    utils.restore_db(engine)
    return engine


@pytest.fixture
def session(engine):
    return sessionmaker(bind=engine)()


@pytest.fixture
def empty_db(engine):
    with contextlib.closing(engine.connect()) as con:
        meta = models.metadata
        trans = con.begin()
        for table in reversed(meta.sorted_tables):
            con.execute(table.delete())
        trans.commit()
    return True


@pytest.fixture
def reset_job_event(engine):
    with contextlib.closing(engine.connect()) as con:
        trans = con.begin()
        con.execute("ALTER SEQUENCE jobs_events_id_seq RESTART WITH 1")
        trans.commit()
    return True


@pytest.fixture
def delete_db(request, engine, teardown_db_clean):
    models.metadata.drop_all(engine)
    engine.execute("DROP TABLE IF EXISTS alembic_version")


@pytest.fixture(scope="session", autouse=True)
def memoize_password_hash():
    def memoize(func):
        cache = {}

        def helper(*args):
            if args in cache:
                return cache[args]
            else:
                value = func(*args)
                cache[args] = value
                return value

        return helper

    pwd_context.verify = memoize(pwd_context.verify)
    pwd_context.hash = memoize(pwd_context.hash)


@pytest.fixture
def teardown_db_clean(request, engine):
    request.addfinalizer(lambda: utils.restore_db(engine))


@pytest.fixture
def fs_clean(request):
    """Clean test file upload directory"""
    request.addfinalizer(utils.rm_upload_folder)


@pytest.fixture
def db_provisioning(empty_db, session):
    utils.provision(session)


@pytest.fixture
def app(db_provisioning, engine, fs_clean):
    app = dci.app.create_app()
    app.testing = True
    app.engine = engine
    return app


@pytest.fixture
def admin(app):
    return utils.generate_client(app, ("admin", "admin"))


@pytest.fixture
def unauthorized(app):
    return utils.generate_client(app, ("bob", "bob"))


@pytest.fixture
def user(app):
    return utils.generate_client(app, ("user", "user"))


@pytest.fixture
def user2(app):
    return utils.generate_client(app, ("user2", "user2"))


@pytest.fixture
def rh_employee(app):
    return utils.generate_client(app, ("rh_employee", "rh_employee"))


@pytest.fixture
def user_sso(app, access_token):
    client = utils.generate_client(app, access_token=access_token)
    # first call, it create the user in the database
    client.get("/api/v1/users/me")
    return client


@pytest.fixture
def user_id(admin):
    user = admin.get("/api/v1/users?where=name:user")
    user = admin.get("/api/v1/users/%s" % user.data["users"][0]["id"]).data
    return str(user["user"]["id"])


@pytest.fixture
def user_no_team(admin):
    r = admin.get("/api/v1/users?where=name:user_no_team")
    return dict(r.data["users"][0])


@pytest.fixture
def epm(app):
    return utils.generate_client(app, ("epm", "epm"))


@pytest.fixture
def epm_id(epm):
    return epm.get("/api/v1/users/me").data["user"]["id"]


@pytest.fixture
def topic_id(admin, team_id, product):
    data = {
        "name": "topic_name",
        "product_id": product["id"],
        "component_types": ["type_1", "type_2", "type_3"],
    }
    topic = admin.post("/api/v1/topics", data=data).data
    t_id = topic["topic"]["id"]
    admin.post("/api/v1/topics/%s/teams" % t_id, data={"team_id": team_id})
    return str(t_id)


@pytest.fixture
def topic(admin, team_user_id, product):
    topic = admin.post(
        "/api/v1/topics",
        data={
            "name": "OSP12",
            "product_id": product["id"],
            "component_types": ["puddle_osp"],
        },
    ).data["topic"]
    admin.post(
        "/api/v1/components",
        data={
            "topic_id": topic["id"],
            "name": "RH7-RHOS-12.0 2017-11-09.2",
            "type": "puddle_osp",
        },
    )
    admin.post("/api/v1/topics/%s/teams" % topic["id"], data={"team_id": team_user_id})
    return topic


@pytest.fixture
def test_id(admin):
    data = {"name": "pname"}
    test = admin.post("/api/v1/tests", data=data).data
    return str(test["test"]["id"])


@pytest.fixture
def team_id(admin, team_user_id):
    team = admin.post("/api/v1/teams", data={"name": "pname"})
    return str(team.data["team"]["id"])


@pytest.fixture
def team_product_id(admin):
    team = admin.get("/api/v1/teams?where=name:product")
    team = admin.get("/api/v1/teams/%s" % team.data["teams"][0]["id"]).data
    return str(team["team"]["id"])


@pytest.fixture
def team_user_id(admin):
    team = admin.get("/api/v1/teams?where=name:user").data["teams"][0]
    return str(team["id"])


@pytest.fixture
def team_user_id2(admin):
    team = admin.get("/api/v1/teams?where=name:user2").data["teams"][0]
    return str(team["id"])


@pytest.fixture
def team_admin_id(admin):
    team = admin.get("/api/v1/teams?where=name:admin").data["teams"][0]
    return str(team["id"])