import { Resource } from "../resource.js";
import { logger } from "../util/logger.js";
import { extractCloudflareResult } from "./api-response.js";
import { createCloudflareApi, } from "./api.js";
/**
 * Manages a batch of DNS records in a Cloudflare zone.
 * Supports creating, updating, and deleting multiple records at once.
 *
 * @example
 * // Create multiple A and CNAME records
 * const dnsRecords = await DnsRecords("example.com-dns", {
 *   zone: "example.com",
 *   records: [
 *     {
 *       name: "www.example.com",
 *       type: "A",
 *       content: "192.0.2.1",
 *       proxied: true
 *     },
 *     {
 *       name: "blog.example.com",
 *       type: "CNAME",
 *       content: "www.example.com",
 *       proxied: true
 *     }
 *   ]
 * });
 *
 * @example
 * // Create MX records for email routing
 * const emailRecords = await DnsRecords("example.com-email", {
 *   zone: "example.com",
 *   records: [
 *     {
 *       name: "example.com",
 *       type: "MX",
 *       content: "aspmx.l.google.com",
 *       priority: 1
 *     },
 *     {
 *       name: "example.com",
 *       type: "MX",
 *       content: "alt1.aspmx.l.google.com",
 *       priority: 5
 *     }
 *   ]
 * });
 */
export const DnsRecords = Resource("cloudflare::DnsRecords", async function (_id, props) {
    // Create Cloudflare API client
    const api = await createCloudflareApi(props);
    // Get zone ID if domain name was provided
    const zoneId = props.zoneId;
    if (this.phase === "delete") {
        if (props.delete !== false && this.output?.records) {
            // Delete all existing records
            await Promise.all(this.output.records.map(async (record) => {
                try {
                    const response = await api.delete(`/zones/${zoneId}/dns_records/${record.id}`);
                    if (!response.ok && response.status !== 404) {
                        logger.error(`Failed to delete DNS record ${record.name}: ${response.statusText}`);
                    }
                }
                catch (error) {
                    logger.error(`Error deleting DNS record ${record.name}:`, error);
                }
            }));
        }
        return this.destroy();
    }
    if (this.phase === "update" && this.output?.records) {
        // Get current records to compare with desired state
        const currentRecords = deduplicateRecords(this.output.records);
        const desiredRecords = deduplicateRecords(props.records);
        // Find records to delete (exist in current but not in desired)
        const recordsToDelete = [];
        for (const [key, record] of currentRecords.entries()) {
            if (!desiredRecords.has(key)) {
                recordsToDelete.push(record);
            }
        }
        // Delete orphaned records (skip if delete: false)
        if (props.delete !== false) {
            await Promise.all(recordsToDelete.map(async (record) => {
                try {
                    const response = await api.delete(`/zones/${zoneId}/dns_records/${record.id}`);
                    if (!response.ok && response.status !== 404) {
                        logger.error(`Failed to delete DNS record ${record.name}: ${response.statusText}`);
                    }
                }
                catch (error) {
                    logger.error(`Error deleting DNS record ${record.name}:`, error);
                }
            }));
        }
        // Update or create records
        const updatedRecords = await Promise.all(desiredRecords.entries().map(async ([key, desired]) => {
            // Find matching existing record
            const existing = currentRecords.get(key);
            if (existing) {
                // Update if content or other properties changed
                if (existing.content !== desired.content ||
                    existing.ttl !== (desired.ttl || 1) ||
                    existing.proxied !== (desired.proxied || false) ||
                    existing.priority !== desired.priority ||
                    existing.comment !== desired.comment) {
                    return createOrUpdateRecord(api, zoneId, desired, existing.id);
                }
                return existing;
            }
            // Create new record
            return createOrUpdateRecord(api, zoneId, desired);
        }));
        return {
            zoneId,
            records: updatedRecords,
        };
    }
    // Create new records
    const uniqueRecords = deduplicateRecords(props.records);
    const createdRecords = await Promise.all(uniqueRecords.entries().map(async ([key, record]) => {
        const existingRecords = await listRecords(api, zoneId, {
            name: record.name,
            type: record.type,
        });
        const existingRecord = existingRecords.find((r) => makeRecordKey(r) === key);
        return createOrUpdateRecord(api, zoneId, record, existingRecord?.id);
    }));
    return {
        zoneId,
        records: createdRecords,
    };
});
function deduplicateRecords(records) {
    const map = new Map();
    for (const record of records) {
        map.set(makeRecordKey(record), record);
    }
    return map;
}
function makeRecordKey(record) {
    // For record types that can have multiple entries with the same name (MX, TXT, NS, etc.),
    // include content and/or priority in the key to avoid deduplication
    let key = `${record.name}-${record.type}`;
    // If it's a record type that can have multiple entries with the same name, make the key unique
    if (["MX", "TXT", "NS", "SRV", "CAA"].includes(record.type)) {
        // For MX, include priority in the key
        if (record.type === "MX" || record.type === "SRV") {
            key = `${key}-${record.priority}-${record.content}`;
        }
        else {
            // For other multi-record types, content is the differentiator
            key = `${key}-${record.content}`;
        }
    }
    return key;
}
/**
 * Create or update a DNS record
 */
async function createOrUpdateRecord(api, zoneId, record, existingId) {
    const payload = getRecordPayload(record);
    const response = await (existingId
        ? api.put(`/zones/${zoneId}/dns_records/${existingId}`, payload)
        : api.post(`/zones/${zoneId}/dns_records`, payload));
    if (!response.ok) {
        const errorBody = await response.text();
        // If it's an update operation and the record doesn't exist, fall back to creation
        if (existingId && response.status === 404) {
            try {
                const createResponse = await api.post(`/zones/${zoneId}/dns_records`, payload);
                if (createResponse.ok) {
                    return convertCloudflareRecord((await createResponse.json()).result, zoneId);
                }
            }
            catch (_err) {
                // Fall through to the original error
            }
        }
        throw new Error(`Failed to ${existingId ? "update" : "create"} DNS record ${record.name}: ${response.statusText}\nResponse: ${errorBody}`);
    }
    const result = (await response.json());
    return convertCloudflareRecord(result.result, zoneId);
}
export async function listRecords(api, zoneId, filter = {}) {
    const queryParams = new URLSearchParams();
    if (filter.type) {
        queryParams.set("type", filter.type);
    }
    if (filter.name) {
        queryParams.set("name", filter.name);
    }
    const queryString = queryParams.size > 0 ? `?${queryParams.toString()}` : "";
    const result = await extractCloudflareResult(`list DNS records for zone ${zoneId}`, api.get(`/zones/${zoneId}/dns_records${queryString}`));
    return result.map((record) => convertCloudflareRecord(record, zoneId));
}
/**
 * Get the record payload for create/update operations
 */
function getRecordPayload(record) {
    return {
        type: record.type,
        name: record.name,
        content: record.content,
        ttl: record.ttl || 1,
        proxied: record.proxied || false,
        priority: record.priority,
        comment: record.comment,
    };
}
/**
 * Convert a Cloudflare DNS record response to our DnsRecord type
 */
function convertCloudflareRecord(record, zoneId) {
    return {
        id: record.id,
        name: record.name,
        type: record.type,
        content: record.content,
        ttl: record.ttl,
        proxied: record.proxied,
        priority: record.priority,
        comment: record.comment,
        tags: record.tags,
        createdAt: new Date(record.created_on).getTime(),
        modifiedAt: new Date(record.modified_on).getTime(),
        zoneId,
    };
}
//# sourceMappingURL=dns-records.js.map