import requests

from .conftest import API_PREFIX
from uuid import uuid4


new_subs_oid = None
new_subs_oid_2 = None

new_user_invite_token = None
existing_user_invite_token = None

VALID_PASSWORD = "ValidPassW0rd!"

invite_email = "test-User@EXample.com"


def test_create_sub_org_invalid_auth(crawler_auth_headers):
    r = requests.post(
        f"{API_PREFIX}/subscriptions/create",
        headers=crawler_auth_headers,
        json={
            "subId": "123",
            "status": "active",
            "firstAdminInviteEmail": invite_email,
            "quotas": {
                "maxPagesPerCrawl": 100,
                "maxConcurrentCrawls": 1,
                "storageQuota": 1000000,
                "maxExecMinutesPerMonth": 1000,
            },
        },
    )

    assert r.status_code == 403


def test_create_sub_org_and_invite_new_user(admin_auth_headers):
    r = requests.post(
        f"{API_PREFIX}/subscriptions/create",
        headers=admin_auth_headers,
        json={
            "subId": "123",
            "status": "active",
            "planId": "basic",
            "firstAdminInviteEmail": invite_email,
            "quotas": {
                "maxPagesPerCrawl": 100,
                "maxConcurrentCrawls": 1,
                "storageQuota": 1000000,
                "maxExecMinutesPerMonth": 1000,
            },
        },
    )

    assert r.status_code == 200
    data = r.json()
    assert data["added"]

    org_id = data["id"]

    assert data["invited"] == "new_user"

    global new_user_invite_token
    new_user_invite_token = data["token"]

    global new_subs_oid
    new_subs_oid = org_id


def test_validate_new_org_with_quotas_and_name_is_uid(admin_auth_headers):
    r = requests.get(f"{API_PREFIX}/orgs/{new_subs_oid}", headers=admin_auth_headers)
    assert r.status_code == 200

    data = r.json()
    assert data["slug"] == data["id"]
    assert data["name"] == data["name"]

    assert data["quotas"] == {
        "maxPagesPerCrawl": 100,
        "maxConcurrentCrawls": 1,
        "storageQuota": 1000000,
        "maxExecMinutesPerMonth": 1000,
        "extraExecMinutes": 0,
        "giftedExecMinutes": 0,
    }
    assert data["subscription"] == {
        "subId": "123",
        "status": "active",
        "planId": "basic",
        "futureCancelDate": None,
        "readOnlyOnCancel": False,
    }


def test_register_with_invite():
    # Create user with invite
    r = requests.post(
        f"{API_PREFIX}/auth/register",
        json={
            "name": "Test User",
            "email": invite_email,
            "password": VALID_PASSWORD,
            "inviteToken": new_user_invite_token,
        },
    )
    assert r.status_code == 201


def test_validate_new_org_with_quotas_and_update_name(admin_auth_headers):
    r = requests.get(f"{API_PREFIX}/orgs/{new_subs_oid}", headers=admin_auth_headers)
    assert r.status_code == 200

    data = r.json()
    assert data["slug"] == "test-users-archive"
    assert data["name"] == "Test User's Archive"

    assert data["quotas"] == {
        "maxPagesPerCrawl": 100,
        "maxConcurrentCrawls": 1,
        "storageQuota": 1000000,
        "maxExecMinutesPerMonth": 1000,
        "extraExecMinutes": 0,
        "giftedExecMinutes": 0,
    }
    assert "subscription" in data


def test_create_sub_org_and_invite_existing_user_dupe_sub(admin_auth_headers):
    r = requests.post(
        f"{API_PREFIX}/subscriptions/create",
        headers=admin_auth_headers,
        json={
            "subId": "123",
            "status": "test",
            "planId": "basic",
            "firstAdminInviteEmail": invite_email,
            "quotas": {
                "maxPagesPerCrawl": 100,
                "maxConcurrentCrawls": 1,
                "storageQuota": 1000000,
                "maxExecMinutesPerMonth": 1000,
            },
        },
    )

    assert r.status_code == 400
    assert r.json()["detail"] == "duplicate_org_subscription.subId"


def test_create_sub_org_and_invite_existing_user(admin_auth_headers):
    r = requests.post(
        f"{API_PREFIX}/subscriptions/create",
        headers=admin_auth_headers,
        json={
            "subId": "234",
            "status": "active",
            "planId": "basic",
            "firstAdminInviteEmail": invite_email,
            "quotas": {
                "maxPagesPerCrawl": 100,
                "maxConcurrentCrawls": 1,
                "storageQuota": 1000000,
                "maxExecMinutesPerMonth": 1000,
            },
        },
    )

    assert r.status_code == 200
    data = r.json()
    assert data["added"]

    org_id = data["id"]

    global new_subs_oid_2
    new_subs_oid_2 = org_id

    assert data["invited"] == "existing_user"

    global existing_user_invite_token
    existing_user_invite_token = data["token"]


def test_login_existing_user_for_invite():
    r = requests.post(
        f"{API_PREFIX}/auth/jwt/login",
        data={
            "username": invite_email,
            "password": VALID_PASSWORD,
            "grant_type": "password",
        },
    )
    data = r.json()
    assert r.status_code == 200
    login_token = data["access_token"]

    auth_headers = {"Authorization": "bearer " + login_token}

    # Get existing user invite to confirm it is valid
    r = requests.get(
        f"{API_PREFIX}/users/me/invite/{existing_user_invite_token}",
        headers=auth_headers,
    )
    assert r.status_code == 200
    data = r.json()
    assert data["firstOrgAdmin"] == True
    assert data["orgName"] == data["oid"]
    assert data["orgName"] == data["orgSlug"]
    assert data["fromSuperuser"] == True
    assert not data["inviterEmail"]
    assert not data["inviterName"]

    # Accept existing user invite
    r = requests.post(
        f"{API_PREFIX}/orgs/invite-accept/{existing_user_invite_token}",
        headers=auth_headers,
    )
    assert r.status_code == 200
    data = r.json()
    assert data["added"]
    org = data["org"]

    assert org["id"] == new_subs_oid_2
    assert org["name"] == "Test User's Archive 2"
    assert org["slug"] == "test-users-archive-2"

    assert org["quotas"] == {
        "maxPagesPerCrawl": 100,
        "maxConcurrentCrawls": 1,
        "storageQuota": 1000000,
        "maxExecMinutesPerMonth": 1000,
        "extraExecMinutes": 0,
        "giftedExecMinutes": 0,
    }
    assert "subscription" in org


def test_update_sub(admin_auth_headers):
    r = requests.post(
        f"{API_PREFIX}/subscriptions/update",
        headers=admin_auth_headers,
        json={
            "subId": "123",
            "status": "paused_payment_failed",
            "planId": "basic",
            "futureCancelDate": "2028-12-26T01:02:03Z",
        },
    )

    assert r.status_code == 200
    assert r.json() == {"updated": True}

    r = requests.get(f"{API_PREFIX}/orgs/{new_subs_oid}", headers=admin_auth_headers)
    assert r.status_code == 200

    data = r.json()

    sub = data["subscription"]
    assert sub == {
        "subId": "123",
        "status": "paused_payment_failed",
        "planId": "basic",
        "futureCancelDate": "2028-12-26T01:02:03Z",
        "readOnlyOnCancel": False,
    }

    assert data["readOnly"] == True
    assert data["readOnlyReason"] == "subscriptionPaused"


def test_update_sub_2(admin_auth_headers):
    r = requests.post(
        f"{API_PREFIX}/subscriptions/update",
        headers=admin_auth_headers,
        json={
            "subId": "123",
            "status": "active",
            "planId": "basic2",
            "futureCancelDate": None,
            # not updateable here, only by superadmin
            "readOnlyOnCancel": True,
            "quotas": {
                "maxPagesPerCrawl": 50,
                "storageQuota": 500000,
            },
        },
    )

    assert r.status_code == 200
    assert r.json() == {"updated": True}

    r = requests.get(f"{API_PREFIX}/orgs/{new_subs_oid}", headers=admin_auth_headers)
    assert r.status_code == 200

    data = r.json()

    sub = data["subscription"]
    assert sub == {
        "subId": "123",
        "status": "active",
        "planId": "basic2",
        "futureCancelDate": None,
        "readOnlyOnCancel": False,
    }

    assert data["readOnly"] == False
    assert data["readOnlyReason"] == ""


def test_get_billing_portal_url(admin_auth_headers, echo_server):
    r = requests.get(
        f"{API_PREFIX}/orgs/{new_subs_oid}/billing-portal", headers=admin_auth_headers
    )
    assert r.status_code == 200

    assert r.json() == {"portalUrl": "https://portal.example.com/path/"}


def test_cancel_sub_and_delete_org(admin_auth_headers):
    # cancel, resulting in org deletion
    r = requests.post(
        f"{API_PREFIX}/subscriptions/cancel",
        headers=admin_auth_headers,
        json={
            "subId": "123",
        },
    )

    assert r.status_code == 200
    assert r.json() == {"canceled": True, "deleted": True}

    r = requests.get(f"{API_PREFIX}/orgs/{new_subs_oid}", headers=admin_auth_headers)
    assert r.status_code == 404
    assert r.json()["detail"] == "org_not_found"


def test_cancel_sub_and_no_delete_org(admin_auth_headers):
    # mark org as read-only on cancel, then cancel to avoid deletion
    r = requests.post(
        f"{API_PREFIX}/orgs/{new_subs_oid_2}/read-only-on-cancel",
        headers=admin_auth_headers,
        json={
            "readOnlyOnCancel": True,
        },
    )
    assert r.status_code == 200
    assert r.json() == {"updated": True}

    r = requests.post(
        f"{API_PREFIX}/subscriptions/cancel",
        headers=admin_auth_headers,
        json={
            "subId": "234",
        },
    )

    assert r.status_code == 200
    assert r.json() == {"canceled": True, "deleted": False}

    r = requests.get(f"{API_PREFIX}/orgs/{new_subs_oid_2}", headers=admin_auth_headers)
    assert r.status_code == 200
    data = r.json()
    assert data["readOnly"] == True
    assert data["readOnlyReason"] == "subscriptionCanceled"

    r = requests.post(
        f"{API_PREFIX}/subscriptions/cancel",
        headers=admin_auth_headers,
        json={
            "subId": "234",
        },
    )
    assert r.status_code == 404
    assert r.json() == {"detail": "org_for_subscription_not_found"}


def test_import_sub_invalid_org(admin_auth_headers):
    r = requests.post(
        f"{API_PREFIX}/subscriptions/import",
        headers=admin_auth_headers,
        json={
            "subId": "345",
            "planId": "basic",
            "status": "active",
            "oid": str(uuid4()),
        },
    )
    assert r.status_code == 400
    assert r.json() == {"detail": "invalid_org_id"}


def test_import_sub_existing_org(admin_auth_headers, non_default_org_id):
    r = requests.post(
        f"{API_PREFIX}/subscriptions/import",
        headers=admin_auth_headers,
        json={
            "subId": "345",
            "planId": "basic",
            "status": "active",
            "oid": non_default_org_id,
        },
    )
    assert r.status_code == 200
    assert r.json() == {"added": True, "id": non_default_org_id}

    r = requests.get(
        f"{API_PREFIX}/orgs/{non_default_org_id}", headers=admin_auth_headers
    )
    assert r.status_code == 200
    data = r.json()
    assert data["subscription"] == {
        "subId": "345",
        "status": "active",
        "planId": "basic",
        "futureCancelDate": None,
        "readOnlyOnCancel": False,
    }


def test_subscription_events_log(admin_auth_headers, non_default_org_id):
    r = requests.get(f"{API_PREFIX}/subscriptions/events", headers=admin_auth_headers)
    assert r.status_code == 200
    data = r.json()
    events = data["items"]
    total = data["total"]

    assert total == 7

    for event in events:
        assert event["timestamp"]
        del event["timestamp"]

    assert events == [
        {
            "type": "create",
            "subId": "123",
            "oid": new_subs_oid,
            "status": "active",
            "planId": "basic",
            "firstAdminInviteEmail": "test-user@example.com",
            "quotas": {
                "maxConcurrentCrawls": 1,
                "maxPagesPerCrawl": 100,
                "storageQuota": 1000000,
                "maxExecMinutesPerMonth": 1000,
                "extraExecMinutes": 0,
                "giftedExecMinutes": 0,
            },
        },
        {
            "type": "create",
            "subId": "234",
            "oid": new_subs_oid_2,
            "status": "active",
            "planId": "basic",
            "firstAdminInviteEmail": "test-user@example.com",
            "quotas": {
                "maxConcurrentCrawls": 1,
                "maxPagesPerCrawl": 100,
                "storageQuota": 1000000,
                "maxExecMinutesPerMonth": 1000,
                "extraExecMinutes": 0,
                "giftedExecMinutes": 0,
            },
        },
        {
            "type": "update",
            "subId": "123",
            "oid": new_subs_oid,
            "status": "paused_payment_failed",
            "planId": "basic",
            "futureCancelDate": "2028-12-26T01:02:03Z",
            "quotas": None,
        },
        {
            "type": "update",
            "subId": "123",
            "oid": new_subs_oid,
            "status": "active",
            "planId": "basic2",
            "futureCancelDate": None,
            "quotas": {
                "maxPagesPerCrawl": 50,
                "storageQuota": 500000,
                "extraExecMinutes": None,
                "giftedExecMinutes": None,
                "maxConcurrentCrawls": None,
                "maxExecMinutesPerMonth": None,
            },
        },
        {"subId": "123", "oid": new_subs_oid, "type": "cancel"},
        {"subId": "234", "oid": new_subs_oid_2, "type": "cancel"},
        {
            "type": "import",
            "subId": "345",
            "oid": non_default_org_id,
            "status": "active",
            "planId": "basic",
        },
    ]


def test_subscription_events_log_filter_sub_id(admin_auth_headers):
    r = requests.get(
        f"{API_PREFIX}/subscriptions/events?subId=123", headers=admin_auth_headers
    )
    assert r.status_code == 200
    data = r.json()
    events = data["items"]
    total = data["total"]

    assert total == 4

    for event in events:
        del event["timestamp"]

    assert events == [
        {
            "type": "create",
            "subId": "123",
            "oid": new_subs_oid,
            "status": "active",
            "planId": "basic",
            "firstAdminInviteEmail": "test-user@example.com",
            "quotas": {
                "maxConcurrentCrawls": 1,
                "maxPagesPerCrawl": 100,
                "storageQuota": 1000000,
                "maxExecMinutesPerMonth": 1000,
                "extraExecMinutes": 0,
                "giftedExecMinutes": 0,
            },
        },
        {
            "type": "update",
            "subId": "123",
            "oid": new_subs_oid,
            "status": "paused_payment_failed",
            "planId": "basic",
            "futureCancelDate": "2028-12-26T01:02:03Z",
            "quotas": None,
        },
        {
            "type": "update",
            "subId": "123",
            "oid": new_subs_oid,
            "status": "active",
            "planId": "basic2",
            "futureCancelDate": None,
            "quotas": {
                "maxPagesPerCrawl": 50,
                "storageQuota": 500000,
                "extraExecMinutes": None,
                "giftedExecMinutes": None,
                "maxConcurrentCrawls": None,
                "maxExecMinutesPerMonth": None,
            },
        },
        {"subId": "123", "oid": new_subs_oid, "type": "cancel"},
    ]


def test_subscription_events_log_filter_oid(admin_auth_headers):
    r = requests.get(
        f"{API_PREFIX}/subscriptions/events?oid={new_subs_oid}",
        headers=admin_auth_headers,
    )
    assert r.status_code == 200
    data = r.json()
    events = data["items"]
    total = data["total"]

    assert total == 4

    for event in events:
        del event["timestamp"]

    assert events == [
        {
            "type": "create",
            "subId": "123",
            "oid": new_subs_oid,
            "status": "active",
            "planId": "basic",
            "firstAdminInviteEmail": "test-user@example.com",
            "quotas": {
                "maxConcurrentCrawls": 1,
                "maxPagesPerCrawl": 100,
                "storageQuota": 1000000,
                "maxExecMinutesPerMonth": 1000,
                "extraExecMinutes": 0,
                "giftedExecMinutes": 0,
            },
        },
        {
            "type": "update",
            "subId": "123",
            "oid": new_subs_oid,
            "status": "paused_payment_failed",
            "planId": "basic",
            "futureCancelDate": "2028-12-26T01:02:03Z",
            "quotas": None,
        },
        {
            "type": "update",
            "subId": "123",
            "oid": new_subs_oid,
            "status": "active",
            "planId": "basic2",
            "futureCancelDate": None,
            "quotas": {
                "maxPagesPerCrawl": 50,
                "storageQuota": 500000,
                "extraExecMinutes": None,
                "giftedExecMinutes": None,
                "maxConcurrentCrawls": None,
                "maxExecMinutesPerMonth": None,
            },
        },
        {"subId": "123", "oid": new_subs_oid, "type": "cancel"},
    ]


def test_subscription_events_log_filter_plan_id(admin_auth_headers):
    r = requests.get(
        f"{API_PREFIX}/subscriptions/events?planId=basic2", headers=admin_auth_headers
    )
    assert r.status_code == 200
    data = r.json()
    events = data["items"]
    total = data["total"]

    assert total == 1

    for event in events:
        del event["timestamp"]

    assert events == [
        {
            "type": "update",
            "subId": "123",
            "oid": new_subs_oid,
            "status": "active",
            "planId": "basic2",
            "futureCancelDate": None,
            "quotas": {
                "maxPagesPerCrawl": 50,
                "storageQuota": 500000,
                "extraExecMinutes": None,
                "giftedExecMinutes": None,
                "maxConcurrentCrawls": None,
                "maxExecMinutesPerMonth": None,
            },
        }
    ]


def test_subscription_events_log_filter_status(admin_auth_headers):
    r = requests.get(
        f"{API_PREFIX}/subscriptions/events?subId=123&status=active",
        headers=admin_auth_headers,
    )
    assert r.status_code == 200
    data = r.json()
    events = data["items"]
    total = data["total"]

    assert total == 2

    for event in events:
        del event["timestamp"]

    assert events == [
        {
            "type": "create",
            "subId": "123",
            "oid": new_subs_oid,
            "status": "active",
            "planId": "basic",
            "firstAdminInviteEmail": "test-user@example.com",
            "quotas": {
                "maxConcurrentCrawls": 1,
                "maxPagesPerCrawl": 100,
                "storageQuota": 1000000,
                "maxExecMinutesPerMonth": 1000,
                "extraExecMinutes": 0,
                "giftedExecMinutes": 0,
            },
        },
        {
            "type": "update",
            "subId": "123",
            "oid": new_subs_oid,
            "status": "active",
            "planId": "basic2",
            "futureCancelDate": None,
            "quotas": {
                "maxPagesPerCrawl": 50,
                "storageQuota": 500000,
                "extraExecMinutes": None,
                "giftedExecMinutes": None,
                "maxConcurrentCrawls": None,
                "maxExecMinutesPerMonth": None,
            },
        },
    ]


def test_subscription_events_log_filter_sort(admin_auth_headers):
    # Timestamp, descending
    r = requests.get(
        f"{API_PREFIX}/subscriptions/events?sortBy=timestamp&sortDirection=-1",
        headers=admin_auth_headers,
    )
    assert r.status_code == 200
    data = r.json()
    events = data["items"]

    last_timestamp = None
    for event in events:
        timestamp = event["timestamp"]
        if last_timestamp:
            assert last_timestamp >= timestamp
        last_timestamp = timestamp

    # subId, ascending
    r = requests.get(
        f"{API_PREFIX}/subscriptions/events?sortBy=subId&sortDirection=1",
        headers=admin_auth_headers,
    )
    assert r.status_code == 200
    data = r.json()
    events = data["items"]

    last_id = None
    for event in events:
        sub_id = event["subId"]
        if last_id:
            assert last_id <= sub_id
        last_id = sub_id

    # subId, descending
    r = requests.get(
        f"{API_PREFIX}/subscriptions/events?sortBy=subId&sortDirection=-1",
        headers=admin_auth_headers,
    )
    assert r.status_code == 200
    data = r.json()
    events = data["items"]

    last_id = None
    for event in events:
        sub_id = event["subId"]
        if last_id:
            assert last_id >= sub_id
        last_id = sub_id

    # oid, ascending
    r = requests.get(
        f"{API_PREFIX}/subscriptions/events?sortBy=oid&sortDirection=1",
        headers=admin_auth_headers,
    )
    assert r.status_code == 200
    data = r.json()
    events = data["items"]

    last_id = None
    for event in events:
        oid = event["oid"]
        if last_id:
            assert last_id <= oid
        last_id = oid

    # oid, descending
    r = requests.get(
        f"{API_PREFIX}/subscriptions/events?sortBy=oid&sortDirection=-1",
        headers=admin_auth_headers,
    )
    assert r.status_code == 200
    data = r.json()
    events = data["items"]

    last_id = None
    for event in events:
        oid = event["oid"]
        if last_id:
            assert last_id >= oid
        last_id = oid

    # Status, ascending
    r = requests.get(
        f"{API_PREFIX}/subscriptions/events?sortBy=status", headers=admin_auth_headers
    )
    assert r.status_code == 200
    data = r.json()
    events = data["items"]

    last_status = None
    for event in events:
        event_status = event.get("status")
        if event_status and last_status:
            assert last_status <= event_status
        if event_status:
            last_status = event_status

    # Status, descending
    r = requests.get(
        f"{API_PREFIX}/subscriptions/events?sortBy=status&sortDirection=-1",
        headers=admin_auth_headers,
    )
    assert r.status_code == 200
    data = r.json()
    events = data["items"]

    last_status = None
    for event in events:
        event_status = event.get("status")
        if event_status and last_status:
            assert last_status >= event_status
        if event_status:
            last_status = event_status

    # planId, ascending
    r = requests.get(
        f"{API_PREFIX}/subscriptions/events?sortBy=planId&sortDirection=1",
        headers=admin_auth_headers,
    )
    assert r.status_code == 200
    data = r.json()
    events = data["items"]

    last_id = None
    for event in events:
        plan_id = event.get("planId")
        if plan_id and last_id:
            assert last_id <= plan_id
        if plan_id:
            last_id = plan_id

    # planId, descending
    r = requests.get(
        f"{API_PREFIX}/subscriptions/events?sortBy=planId&sortDirection=-1",
        headers=admin_auth_headers,
    )
    assert r.status_code == 200
    data = r.json()
    events = data["items"]

    last_id = None
    for event in events:
        plan_id = event.get("planId")
        if plan_id and last_id:
            assert last_id >= plan_id
        if plan_id:
            last_id = plan_id

    # futureCancelDate, ascending
    r = requests.get(
        f"{API_PREFIX}/subscriptions/events?sortBy=futureCancelDate&sortDirection=1",
        headers=admin_auth_headers,
    )
    assert r.status_code == 200
    data = r.json()
    events = data["items"]

    last_date = None
    for event in events:
        cancel_date = event.get("futureCancelDate")
        if cancel_date and last_date:
            assert last_id <= cancel_date
        if cancel_date:
            last_date = cancel_date

    # futureCancelDate, descending
    r = requests.get(
        f"{API_PREFIX}/subscriptions/events?sortBy=futureCancelDate&sortDirection=-1",
        headers=admin_auth_headers,
    )
    assert r.status_code == 200
    data = r.json()
    events = data["items"]

    last_date = None
    for event in events:
        cancel_date = event.get("futureCancelDate")
        if cancel_date and last_date:
            assert last_id >= cancel_date
        if cancel_date:
            last_date = cancel_date
