import http from 'k6/http';
import { check } from 'k6';

/**
 * @typedef {Object} AuthHeaders
 * @property {string} 'Content-Type' - Content type header
 * @property {string} 'Cookie' - Session cookie
 * @property {string} 'X-Appwrite-Project' - Project ID header
 */

/**
 * @typedef {Object} ApiHeaders
 * @property {string} 'Content-Type' - Content type header
 * @property {string} 'X-Appwrite-Project' - Project ID header
 * @property {string} 'X-Appwrite-Key' - API key header
 */

/**
 * @typedef {Object} ProvisionedResources
 * @property {string} userId - The ID of the created user
 * @property {string} teamId - The ID of the created team
 * @property {string} projectId - The ID of the created project
 * @property {string} cookies - Session cookies for authentication
 * @property {AuthHeaders} headers - Headers for cookie-based authentication
 * @property {string} apiKey - The API key secret
 * @property {ApiHeaders} apiHeaders - Headers for API key authentication
 */

function assert(response, checkName, condition) {
    const result = check(response, {
        [checkName]: condition
    });
    if (!result) {
        console.error(`Assertion failed: ${checkName}`);
        console.error(`Response status: ${response.status}`);
        console.error(`Response body: ${response.body}`);
        throw new Error(`Assertion failed: ${checkName}`);
    }
}

/**
 * Provisions an Appwrite project setup including:
 * - Account creation
 * - Session creation
 * - Team creation
 * - Project creation
 * - API Key creation
 * 
 * @param {Object} config Configuration object
 * @param {string} config.endpoint Base endpoint URL (e.g., 'http://localhost:80/v1')
 * @param {string} config.email Email for account creation
 * @param {string} config.password Password for account creation
 * @param {string} config.name Name for account creation
 * @param {string} config.projectName Name for the project
 * @returns {ProvisionedResources} Object containing all created resource IDs and session information
 */
export function provisionProject(config) {
    const {
        endpoint,
        email,
        password,
        name,
        projectName,
    } = config;

    // Step 1: Create Account
    const accountResponse = http.post(`${endpoint}/account`, JSON.stringify({
        userId: 'unique()',
        email,
        password,
        name
    }), {
        headers: {
            'Content-Type': 'application/json',
        }
    });

    assert(accountResponse, 'account created successfully', (r) => r.status === 201 || r.status === 409);

    const userId = accountResponse.json('$id');

    // Step 2: Create Session
    const sessionResponse = http.post(`${endpoint}/account/sessions/email`, JSON.stringify({
        email,
        password
    }), {
        headers: {
            'Content-Type': 'application/json',
        }
    });

    assert(sessionResponse, 'session created successfully', (r) => r.status === 201);

    // Keep manual control of the cookies to allow for simultaneous requests
    const jar = http.cookieJar();
    jar.clear(`${endpoint}`);

    // Extract cookies for subsequent requests
    const cookies = sessionResponse.headers['Set-Cookie'];

    // Common headers for authenticated requests
    const authHeaders = {
        'Content-Type': 'application/json',
        'Cookie': cookies
    };

    // Step 3: Create Team
    const teamResponse = http.post(`${endpoint}/teams`, JSON.stringify({
        teamId: 'unique()',
        name: `${projectName} Team`
    }), {
        headers: authHeaders
    });

    assert(teamResponse, 'team created successfully', (r) => r.status === 201);

    const teamId = teamResponse.json('$id');

    // Step 4: Create Project
    const projectResponse = http.post(`${endpoint}/projects`, JSON.stringify({
        projectId: 'unique()',
        name: projectName,
        teamId: teamId
    }), {
        headers: authHeaders
    });

    assert(projectResponse, 'project created successfully', (r) => r.status === 201);

    const projectId = projectResponse.json('$id');

    // Step 5: Create API Key
    const apiKeyResponse = http.post(`${endpoint}/projects/${projectId}/keys`, JSON.stringify({
        name: 'Test API Key',
        scopes: SCOPES, // All permissions
    }), {
        headers: authHeaders
    });

    assert(apiKeyResponse, 'api key created successfully', (r) => r.status === 201);

    const apiKey = apiKeyResponse.json('secret');

    // Create a new headers object for API key authentication
    const apiHeaders = {
        'Content-Type': 'application/json',
        'X-Appwrite-Project': projectId,
        'X-Appwrite-Key': apiKey
    };

    // Return all created resources and session info
    return {
        endpoint,
        userId,
        teamId,
        projectId,
        cookies,
        headers: authHeaders,
        apiKey,
        apiHeaders
    };
}

/**
 * Example usage:
 * 
 * const config = {
 *     endpoint: 'http://localhost:80/v1',
 *     email: 'test@example.com',
 *     password: 'complex-password',
 *     name: 'Test User',
 *     projectName: 'Test Project'
 * };
 * 
 * const resources = provisionProject(config);
 */

const SCOPES = [
    "sessions.write",
    "users.read",
    "users.write",
    "teams.read",
    "teams.write",
    "databases.read",
    "databases.write",
    "collections.read",
    "collections.write",
    "attributes.read",
    "attributes.write",
    "indexes.read",
    "indexes.write",
    "documents.read",
    "documents.write",
    "files.read",
    "files.write",
    "buckets.read",
    "buckets.write",
    "functions.read",
    "functions.write",
    "executions.read",
    "executions.write",
    "targets.read",
    "targets.write",
    "providers.read",
    "providers.write",
    "messages.read",
    "messages.write",
    "topics.read",
    "topics.write",
    "subscribers.read",
    "subscribers.write",
    "locale.read",
    "avatars.read",
    "health.read",
    "migrations.read",
    "migrations.write"
]

export function provisionDatabase(config) {
    const {
        endpoint,
        apiHeaders
    } = config;

    // Create database
    const databaseResponse = http.post(
        `${endpoint}/databases`,
        JSON.stringify({
            databaseId: 'unique()',
            name: 'Bulk Test DB'
        }),
        { headers: apiHeaders }
    );

    assert(databaseResponse, 'database created successfully', (r) => r.status === 201);

    const databaseId = databaseResponse.json('$id');

    // Create collection
    const collectionResponse = http.post(
        `${endpoint}/databases/${databaseId}/collections`,
        JSON.stringify({
            collectionId: 'unique()',
            name: 'Bulk Test Collection',
            permissions: ['read("any")', 'write("any")'],
            documentSecurity: false
        }),
        { headers: apiHeaders }
    );

    assert(collectionResponse, 'collection created successfully', (r) => r.status === 201);

    const collectionId = collectionResponse.json('$id');

    // Create name attribute
    const nameAttributeResponse = http.post(
        `${endpoint}/databases/${databaseId}/collections/${collectionId}/attributes/string`,
        JSON.stringify({
            key: 'name',
            size: 100,
            required: false,
            default: null,
            array: false,
            encrypt: false
        }),
        { headers: apiHeaders }
    );

    assert(nameAttributeResponse, 'name attribute created successfully', (r) => r.status === 202);

    // Create age attribute
    const ageAttributeResponse = http.post(
        `${endpoint}/databases/${databaseId}/collections/${collectionId}/attributes/integer`,
        JSON.stringify({
            key: 'age',
            required: false,
        }),
        { headers: apiHeaders }
    );

    assert(ageAttributeResponse, 'age attribute created successfully', (r) => r.status === 202);

    // Create email attribute
    const emailAttributeResponse = http.post(
        `${endpoint}/databases/${databaseId}/collections/${collectionId}/attributes/email`,
        JSON.stringify({
            key: 'email',
            required: false,
        }),
        { headers: apiHeaders }
    );

    assert(emailAttributeResponse, 'email attribute created successfully', (r) => r.status === 202);

    // Create height attribute
    const heightAttributeResponse = http.post(
        `${endpoint}/databases/${databaseId}/collections/${collectionId}/attributes/float`,
        JSON.stringify({
            key: 'height',
            required: false,
        }),
        { headers: apiHeaders }
    );

    assert(heightAttributeResponse, 'height attribute created successfully', (r) => r.status === 202);

    return {
        databaseId,
        collectionId
    };
}

export function cleanup(config) {
    const {
        endpoint,
        teamId,
        headers
    } = config;

    // Delete Organization
    const organizationResponse = http.del(
        `${endpoint}/teams/${teamId}`,
        null,
        {
            headers
        }
    );

    assert(organizationResponse, 'organization deleted successfully', (r) => r.status === 204);
}

export function unique() {
    const timestamp = Date.now().toString(36);
    const randomPart = Math.random().toString(36).substring(2, 15);
    return `${timestamp}-${randomPart}`;
}