import { randomUUID } from 'node:crypto';
import { ConfigManager } from '../config/manager.js';
import { AutomationManager } from './manager.js';
import { AutomationJobStore } from './store/jobs.js';
import { AutomationRouteStore } from './store/routes.js';
import { AutomationRunStore } from './store/runs.js';
import { AutomationSourceStore } from './store/sources.js';
function sortJobs(jobs) {
    return [...jobs].sort((a, b) => a.name.localeCompare(b.name) || a.createdAt - b.createdAt);
}
function sortRuns(runs) {
    return [...runs].sort((a, b) => b.queuedAt - a.queuedAt);
}
function sortRoutes(routes) {
    return [...routes].sort((a, b) => b.lastSeenAt - a.lastSeenAt || a.id.localeCompare(b.id));
}
function sortSources(sources) {
    return [...sources].sort((a, b) => a.label.localeCompare(b.label) || a.createdAt - b.createdAt);
}
export class AutomationService {
    jobStore;
    runStore;
    routeStore;
    sourceStore;
    manager;
    jobs = new Map();
    runs = new Map();
    routes = new Map();
    sources = new Map();
    loaded = false;
    constructor(config) {
        const configManager = config.configManager;
        this.jobStore = config.jobs ?? new AutomationJobStore({ configManager });
        this.runStore = config.runs ?? new AutomationRunStore({ configManager });
        this.routeStore = config.routes ?? new AutomationRouteStore({ configManager });
        this.sourceStore = config.sources ?? new AutomationSourceStore({ configManager });
        if (!config.manager) {
            throw new Error('AutomationService requires an explicit AutomationManager instance');
        }
        this.manager = config.manager;
    }
    async start() {
        await this.manager.start();
    }
    stop() {
        this.manager.stop();
    }
    async load() {
        if (this.loaded)
            return;
        const [jobSnapshot, runSnapshot, routeSnapshot, sourceSnapshot] = await Promise.all([
            this.jobStore.load(),
            this.runStore.load(),
            this.routeStore.load(),
            this.sourceStore.load(),
        ]);
        this.jobs.clear();
        this.runs.clear();
        this.routes.clear();
        this.sources.clear();
        for (const job of jobSnapshot.jobs)
            this.jobs.set(job.id, job);
        for (const run of runSnapshot.runs)
            this.runs.set(run.id, run);
        for (const route of routeSnapshot.routes)
            this.routes.set(route.id, route);
        for (const source of sourceSnapshot.sources)
            this.sources.set(source.id, source);
        this.loaded = true;
    }
    listJobs() {
        return sortJobs(this.jobs.values());
    }
    listRuns(jobId) {
        const runs = jobId
            ? [...this.runs.values()].filter((run) => run.jobId === jobId)
            : this.runs.values();
        return sortRuns(runs);
    }
    listRoutes() {
        return sortRoutes(this.routes.values());
    }
    listSources() {
        return sortSources(this.sources.values());
    }
    getJob(jobId) {
        return this.jobs.get(jobId);
    }
    getRoute(routeId) {
        return this.routes.get(routeId);
    }
    getSource(sourceId) {
        return this.sources.get(sourceId);
    }
    async upsertJob(job) {
        await this.load();
        this.jobs.set(job.id, job);
        if (job.source) {
            this.sources.set(job.source.id, job.source);
        }
        await Promise.all([this.saveJobs(), this.saveSources()]);
        return job;
    }
    async upsertRun(run) {
        await this.load();
        this.runs.set(run.id, run);
        this.sources.set(run.triggeredBy.id, run.triggeredBy);
        if (run.route) {
            this.routes.set(run.route.id, run.route);
        }
        await Promise.all([this.saveRuns(), this.saveRoutes(), this.saveSources()]);
        return run;
    }
    async appendRun(run) {
        const timestamp = run.createdAt ?? Date.now();
        const persisted = {
            ...run,
            id: run.id ?? `run-${randomUUID().slice(0, 8)}`,
            updatedAt: run.updatedAt ?? timestamp,
        };
        return await this.upsertRun(persisted);
    }
    async upsertRoute(route) {
        await this.load();
        this.routes.set(route.id, route);
        await this.saveRoutes();
        return route;
    }
    async upsertSource(source) {
        await this.load();
        this.sources.set(source.id, source);
        await this.saveSources();
        return source;
    }
    async removeJob(jobId) {
        await this.load();
        const removed = this.jobs.delete(jobId);
        if (!removed)
            return false;
        for (const [runId, run] of this.runs) {
            if (run.jobId === jobId)
                this.runs.delete(runId);
        }
        for (const [routeId, route] of this.routes) {
            if (route.jobId === jobId)
                this.routes.delete(routeId);
        }
        await Promise.all([this.saveJobs(), this.saveRuns(), this.saveRoutes()]);
        return true;
    }
    async removeRoute(routeId) {
        await this.load();
        const removed = this.routes.delete(routeId);
        if (!removed)
            return false;
        await this.saveRoutes();
        return true;
    }
    async removeSource(sourceId) {
        await this.load();
        const removed = this.sources.delete(sourceId);
        if (!removed)
            return false;
        await this.saveSources();
        return true;
    }
    async saveJobs() {
        await this.jobStore.save(sortJobs(this.jobs.values()));
    }
    async saveRuns() {
        await this.runStore.save(sortRuns(this.runs.values()));
    }
    async saveRoutes() {
        await this.routeStore.save(sortRoutes(this.routes.values()));
    }
    async saveSources() {
        await this.sourceStore.save(sortSources(this.sources.values()));
    }
}
