const openApiSpec = {
    openapi: "3.0.3",
    info: {
        title: "Ultimate History Map API",
        version: "1.0.0",
        description: "Read APIs plus section review workflow. Direct geometry/entity mutations are intentionally not exposed.",
    },
    servers: [
        {
            url: "http://localhost:3000",
            description: "Local",
        },
    ],
    tags: [
        { name: "System", description: "Health and OpenAPI endpoints" },
        { name: "Tiles", description: "Vector and raster tile endpoints" },
        { name: "Geometries", description: "Published geometry read endpoints" },
        { name: "Entities", description: "Published entity read endpoints" },
        { name: "Sections", description: "Draft, commit, and submit workflow" },
        { name: "Submissions", description: "Submission review workflow" },
    ],
    components: {
        schemas: {
            ErrorResponse: {
                type: "object",
                properties: {
                    status: { type: "string", enum: ["error"] },
                    data: { nullable: true },
                    message: { type: "string" },
                    errors: {
                        type: "array",
                        items: {},
                    },
                },
                required: ["status", "data", "message", "errors"],
            },
            Entity: {
                type: "object",
                properties: {
                    id: { type: "string" },
                    name: { type: "string" },
                    slug: { type: "string", nullable: true },
                    description: { type: "string", nullable: true },
                    type_id: { type: "string", nullable: true },
                    status: { type: "number", nullable: true },
                    geometry_count: { type: "number" },
                    created_at: { type: "string", nullable: true },
                    updated_at: { type: "string", nullable: true },
                },
                required: ["id", "name", "geometry_count"],
            },
            GeoJSONGeometry: {
                type: "object",
                properties: {
                    type: { type: "string" },
                    coordinates: { type: "array", items: {} },
                },
                required: ["type", "coordinates"],
            },
            GeometryFeature: {
                type: "object",
                properties: {
                    type: { type: "string", enum: ["Feature"] },
                    properties: { type: "object" },
                    geometry: { $ref: "#/components/schemas/GeoJSONGeometry" },
                },
                required: ["type", "properties", "geometry"],
            },
            GeometryFeatureCollection: {
                type: "object",
                properties: {
                    type: { type: "string", enum: ["FeatureCollection"] },
                    features: {
                        type: "array",
                        items: { $ref: "#/components/schemas/GeometryFeature" },
                    },
                },
                required: ["type", "features"],
            },
            Section: {
                type: "object",
                properties: {
                    id: { type: "string" },
                    title: { type: "string" },
                    description: { type: "string", nullable: true },
                    user_id: { type: "string", nullable: true },
                    created_by: { type: "string", nullable: true },
                    state: { type: "object" },
                },
                required: ["id", "title", "state"],
            },
            SectionCommit: {
                type: "object",
                properties: {
                    id: { type: "string" },
                    section_id: { type: "string" },
                    commit_no: { type: "number" },
                    kind: { type: "string" },
                    snapshot: { type: "object", nullable: true },
                },
                required: ["id", "section_id", "commit_no", "kind"],
            },
            SectionSubmission: {
                type: "object",
                properties: {
                    id: { type: "string" },
                    section_id: { type: "string" },
                    commit_id: { type: "string" },
                    status: { type: "string" },
                    snapshot: { type: "object", nullable: true },
                },
                required: ["id", "section_id", "commit_id", "status"],
            },
        },
    },
    paths: {
        "/": {
            get: {
                tags: ["System"],
                summary: "Health check",
                responses: { 200: { description: "Server is running" } },
            },
        },
        "/docs.json": {
            get: {
                tags: ["System"],
                summary: "OpenAPI JSON",
                responses: { 200: { description: "OpenAPI document" } },
            },
        },
        "/tiles/metadata/info": {
            get: {
                tags: ["Tiles"],
                summary: "Vector tile metadata",
                responses: { 200: { description: "MBTiles metadata" } },
            },
        },
        "/tiles/{z}/{x}/{y}": {
            get: {
                tags: ["Tiles"],
                summary: "Vector tile",
                parameters: tileParameters(),
                responses: {
                    200: { description: "Tile binary" },
                    400: { description: "Invalid tile coordinates" },
                    404: { description: "Tile not found" },
                },
            },
        },
        "/raster-tiles/metadata/info": {
            get: {
                tags: ["Tiles"],
                summary: "Raster tile metadata",
                responses: { 200: { description: "MBTiles metadata" } },
            },
        },
        "/raster-tiles/{z}/{x}/{y}": {
            get: {
                tags: ["Tiles"],
                summary: "Raster tile",
                parameters: tileParameters(),
                responses: {
                    200: { description: "Tile binary" },
                    400: { description: "Invalid tile coordinates" },
                    404: { description: "Tile not found" },
                },
            },
        },
        "/geometries": {
            get: {
                tags: ["Geometries"],
                summary: "Query published geometries by bbox, time, and entity",
                parameters: [
                    queryParam("minLng", "number", true),
                    queryParam("minLat", "number", true),
                    queryParam("maxLng", "number", true),
                    queryParam("maxLat", "number", true),
                    queryParam("time", "integer", false),
                    queryParam("entity_id", "string", false),
                ],
                responses: {
                    200: {
                        description: "Feature collection",
                        content: {
                            "application/json": {
                                schema: { $ref: "#/components/schemas/GeometryFeatureCollection" },
                            },
                        },
                    },
                    400: responseRef("Invalid query"),
                },
            },
        },
        "/entities": {
            get: {
                tags: ["Entities"],
                summary: "List published entities",
                parameters: [queryParam("q", "string", false)],
                responses: {
                    200: arrayResponse("Entity list", "#/components/schemas/Entity"),
                },
            },
        },
        "/entities/search": {
            get: {
                tags: ["Entities"],
                summary: "Search published entities by name",
                parameters: [
                    queryParam("name", "string", true),
                    queryParam("limit", "integer", false),
                ],
                responses: {
                    200: arrayResponse("Matched entities", "#/components/schemas/Entity"),
                },
            },
        },
        "/entities/{id}": {
            get: {
                tags: ["Entities"],
                summary: "Get published entity by id",
                parameters: [pathParam("id")],
                responses: {
                    200: objectResponse("Entity", "#/components/schemas/Entity"),
                    404: responseRef("Entity not found"),
                },
            },
        },
        "/sections": {
            get: {
                tags: ["Sections"],
                summary: "List sections",
                responses: { 200: arrayResponse("Sections", "#/components/schemas/Section") },
            },
            post: {
                tags: ["Sections"],
                summary: "Create section",
                requestBody: jsonBody({
                    type: "object",
                    required: ["title"],
                    properties: {
                        id: { type: "string" },
                        title: { type: "string" },
                        description: { type: "string", nullable: true },
                        user_id: { type: "string" },
                        created_by: { type: "string" },
                    },
                }),
                responses: {
                    201: objectResponse("Created section", "#/components/schemas/Section"),
                    400: responseRef("Invalid payload"),
                    409: responseRef("Section id already exists"),
                },
            },
        },
        "/sections/{sectionId}/editor": {
            get: {
                tags: ["Sections"],
                summary: "Open section editor and acquire lock",
                parameters: [
                    pathParam("sectionId"),
                    queryParam("user_id", "string", false),
                ],
                responses: {
                    200: { description: "Editor payload" },
                    404: responseRef("Section not found"),
                    409: responseRef("Section locked by another user"),
                },
            },
        },
        "/sections/{sectionId}/lock": sectionActorPost("Acquire section lock"),
        "/sections/{sectionId}/unlock": sectionActorPost("Release section lock"),
        "/sections/{sectionId}/commits": {
            get: {
                tags: ["Sections"],
                summary: "List section commits",
                parameters: [
                    pathParam("sectionId"),
                    queryParam("include_snapshot", "string", false),
                ],
                responses: { 200: arrayResponse("Commits", "#/components/schemas/SectionCommit") },
            },
            post: {
                tags: ["Sections"],
                summary: "Create reviewed workflow commit",
                description: "Direct geometry/entity mutations are not exposed. Commit snapshots are the mutation path for geometry/entity changes.",
                parameters: [pathParam("sectionId")],
                requestBody: jsonBody({
                    type: "object",
                    required: ["snapshot", "created_by"],
                    properties: {
                        snapshot: { type: "object" },
                        created_by: { type: "string" },
                        expected_version: { type: "number" },
                        expected_head_commit_id: { type: "string", nullable: true },
                        title: { type: "string", nullable: true },
                        note: { type: "string", nullable: true },
                    },
                }),
                responses: {
                    201: { description: "Created commit" },
                    400: responseRef("Invalid snapshot"),
                    409: responseRef("Section conflict"),
                },
            },
        },
        "/sections/{sectionId}/restore": {
            post: {
                tags: ["Sections"],
                summary: "Restore a previous commit",
                parameters: [pathParam("sectionId")],
                responses: {
                    201: { description: "Restore commit created" },
                    400: responseRef("Invalid payload"),
                    404: responseRef("Commit not found"),
                    409: responseRef("Section conflict"),
                },
            },
        },
        "/sections/{sectionId}/submit": {
            post: {
                tags: ["Sections"],
                summary: "Submit head commit for review",
                parameters: [pathParam("sectionId")],
                responses: {
                    201: objectResponse("Submission", "#/components/schemas/SectionSubmission"),
                    400: responseRef("Invalid payload"),
                    409: responseRef("Section conflict"),
                },
            },
        },
        "/sections/{sectionId}/submissions": {
            get: {
                tags: ["Sections"],
                summary: "List section submissions",
                parameters: [
                    pathParam("sectionId"),
                    queryParam("include_snapshot", "string", false),
                ],
                responses: { 200: arrayResponse("Submissions", "#/components/schemas/SectionSubmission") },
            },
        },
        "/submissions/{submissionId}/approve": submissionReviewPost("Approve submission"),
        "/submissions/{submissionId}/reject": submissionReviewPost("Reject submission"),
    },
};

function pathParam(name) {
    return { name, in: "path", required: true, schema: { type: "string" } };
}

function queryParam(name, type, required) {
    return { name, in: "query", required, schema: { type } };
}

function tileParameters() {
    return [pathParam("z"), pathParam("x"), pathParam("y")];
}

function responseRef(description) {
    return {
        description,
        content: {
            "application/json": {
                schema: { $ref: "#/components/schemas/ErrorResponse" },
            },
        },
    };
}

function objectResponse(description, ref) {
    return {
        description,
        content: {
            "application/json": {
                schema: successEnvelopeSchema({ $ref: ref }),
            },
        },
    };
}

function arrayResponse(description, itemRef) {
    return {
        description,
        content: {
            "application/json": {
                schema: successEnvelopeSchema({
                    type: "array",
                    items: { $ref: itemRef },
                }),
            },
        },
    };
}

function successEnvelopeSchema(dataSchema) {
    return {
        type: "object",
        properties: {
            status: { type: "string", enum: ["success"] },
            data: dataSchema,
            message: { type: "string" },
            errors: {
                type: "array",
                items: {},
            },
        },
        required: ["status", "data", "message", "errors"],
    };
}

function jsonBody(schema) {
    return {
        required: true,
        content: {
            "application/json": { schema },
        },
    };
}

function sectionActorPost(summary) {
    return {
        post: {
            tags: ["Sections"],
            summary,
            parameters: [pathParam("sectionId")],
            requestBody: jsonBody({
                type: "object",
                properties: {
                    user_id: { type: "string" },
                    user: { type: "string" },
                },
            }),
            responses: {
                200: { description: "Updated section state" },
                400: responseRef("user_id is required"),
                404: responseRef("Section not found"),
                409: responseRef("Section conflict"),
            },
        },
    };
}

function submissionReviewPost(summary) {
    return {
        post: {
            tags: ["Submissions"],
            summary,
            parameters: [pathParam("submissionId")],
            requestBody: jsonBody({
                type: "object",
                properties: {
                    reviewed_by: { type: "string" },
                    user_id: { type: "string" },
                    review_note: { type: "string", nullable: true },
                },
            }),
            responses: {
                200: objectResponse("Updated submission", "#/components/schemas/SectionSubmission"),
                404: responseRef("Submission not found"),
                409: responseRef("Submission conflict"),
            },
        },
    };
}

module.exports = {
    openApiSpec,
};
