#!/usr/bin/env python3

"""
Test notification_urls validation in Watch and Tag API endpoints.
Ensures that invalid AppRise URLs are rejected when setting notification_urls.

Valid AppRise notification URLs use specific protocols like:
- posts://example.com - POST to HTTP endpoint
- gets://example.com - GET to HTTP endpoint
- mailto://user@example.com - Email
- slack://token/channel - Slack
- discord://webhook_id/webhook_token - Discord
- etc.

Invalid notification URLs:
- https://example.com - Plain HTTPS is NOT a valid AppRise notification protocol
- ftp://example.com - FTP is NOT a valid AppRise notification protocol
- Plain URLs without proper AppRise protocol prefix
"""

from flask import url_for
import json


def test_watch_notification_urls_validation(client, live_server, measure_memory_usage, datastore_path):
    """Test that Watch PUT/POST endpoints validate notification_urls."""
    api_key = live_server.app.config['DATASTORE'].data['settings']['application'].get('api_access_token')

    # Test 1: Create a watch with valid notification URLs
    valid_urls = ["posts://example.com/notify1", "posts://example.com/notify2"]
    res = client.post(
        url_for("createwatch"),
        data=json.dumps({
            "url": "https://example.com",
            "notification_urls": valid_urls
        }),
        headers={'content-type': 'application/json', 'x-api-key': api_key}
    )
    assert res.status_code == 201, "Should accept valid notification URLs on watch creation"
    watch_uuid = res.json['uuid']

    # Verify the notification URLs were saved
    res = client.get(
        url_for("watch", uuid=watch_uuid),
        headers={'x-api-key': api_key}
    )
    assert res.status_code == 200
    assert set(res.json['notification_urls']) == set(valid_urls), "Valid notification URLs should be saved"

    # Test 2: Try to create a watch with invalid notification URLs (https:// is not valid)
    invalid_urls = ["https://example.com/webhook"]
    res = client.post(
        url_for("createwatch"),
        data=json.dumps({
            "url": "https://example.com",
            "notification_urls": invalid_urls
        }),
        headers={'content-type': 'application/json', 'x-api-key': api_key}
    )
    assert res.status_code == 400, "Should reject https:// notification URLs (not a valid AppRise protocol)"
    assert b"is not a valid AppRise URL" in res.data, "Should provide AppRise validation error message"

    # Test 2b: Also test other invalid protocols
    invalid_urls_ftp = ["ftp://not-apprise-url"]
    res = client.post(
        url_for("createwatch"),
        data=json.dumps({
            "url": "https://example.com",
            "notification_urls": invalid_urls_ftp
        }),
        headers={'content-type': 'application/json', 'x-api-key': api_key}
    )
    assert res.status_code == 400, "Should reject ftp:// notification URLs"
    assert b"is not a valid AppRise URL" in res.data, "Should provide AppRise validation error message"

    # Test 3: Update watch with valid notification URLs
    new_valid_urls = ["posts://newserver.com"]
    res = client.put(
        url_for("watch", uuid=watch_uuid),
        data=json.dumps({"notification_urls": new_valid_urls}),
        headers={'content-type': 'application/json', 'x-api-key': api_key}
    )
    assert res.status_code == 200, "Should accept valid notification URLs on watch update"

    # Verify the notification URLs were updated
    res = client.get(
        url_for("watch", uuid=watch_uuid),
        headers={'x-api-key': api_key}
    )
    assert res.status_code == 200
    assert res.json['notification_urls'] == new_valid_urls, "Valid notification URLs should be updated"

    # Test 4: Try to update watch with invalid notification URLs (plain https:// not valid)
    invalid_https_url = ["https://example.com/webhook"]
    res = client.put(
        url_for("watch", uuid=watch_uuid),
        data=json.dumps({"notification_urls": invalid_https_url}),
        headers={'content-type': 'application/json', 'x-api-key': api_key}
    )
    assert res.status_code == 400, "Should reject https:// notification URLs on watch update"
    assert b"is not a valid AppRise URL" in res.data, "Should provide AppRise validation error message"

    # Test 5: Update watch with non-list notification_urls (caught by OpenAPI schema validation)
    res = client.put(
        url_for("watch", uuid=watch_uuid),
        data=json.dumps({"notification_urls": "not-a-list"}),
        headers={'content-type': 'application/json', 'x-api-key': api_key}
    )
    assert res.status_code == 400, "Should reject non-list notification_urls"
    assert b"Validation failed" in res.data or b"is not of type" in res.data

    # Test 6: Verify original URLs are preserved after failed update
    res = client.get(
        url_for("watch", uuid=watch_uuid),
        headers={'x-api-key': api_key}
    )
    assert res.status_code == 200
    assert res.json['notification_urls'] == new_valid_urls, "URLs should remain unchanged after validation failure"


def test_tag_notification_urls_validation(client, live_server, measure_memory_usage, datastore_path):
    """Test that Tag PUT endpoint validates notification_urls."""
    from changedetectionio.model import Tag

    api_key = live_server.app.config['DATASTORE'].data['settings']['application'].get('api_access_token')
    datastore = live_server.app.config['DATASTORE']

    # Create a tag
    tag_uuid = datastore.add_tag(title="Test Tag")
    assert tag_uuid is not None

    # Test 1: Update tag with valid notification URLs
    valid_urls = ["posts://example.com/tag-notify"]
    res = client.put(
        url_for("tag", uuid=tag_uuid),
        data=json.dumps({"notification_urls": valid_urls}),
        headers={'content-type': 'application/json', 'x-api-key': api_key}
    )
    assert res.status_code == 200, "Should accept valid notification URLs on tag update"

    # Verify the notification URLs were saved
    tag = datastore.data['settings']['application']['tags'][tag_uuid]
    assert tag['notification_urls'] == valid_urls, "Valid notification URLs should be saved to tag"

    # Test 2: Try to update tag with invalid notification URLs (https:// not valid)
    invalid_urls = ["https://example.com/webhook"]
    res = client.put(
        url_for("tag", uuid=tag_uuid),
        data=json.dumps({"notification_urls": invalid_urls}),
        headers={'content-type': 'application/json', 'x-api-key': api_key}
    )
    assert res.status_code == 400, "Should reject https:// notification URLs on tag update"
    assert b"is not a valid AppRise URL" in res.data, "Should provide AppRise validation error message"

    # Test 3: Update tag with non-list notification_urls (caught by OpenAPI schema validation)
    res = client.put(
        url_for("tag", uuid=tag_uuid),
        data=json.dumps({"notification_urls": "not-a-list"}),
        headers={'content-type': 'application/json', 'x-api-key': api_key}
    )
    assert res.status_code == 400, "Should reject non-list notification_urls"
    assert b"Validation failed" in res.data or b"is not of type" in res.data

    # Test 4: Verify original URLs are preserved after failed update
    tag = datastore.data['settings']['application']['tags'][tag_uuid]
    assert tag['notification_urls'] == valid_urls, "URLs should remain unchanged after validation failure"
