"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
    return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __importStar = (this && this.__importStar) || (function () {
    var ownKeys = function(o) {
        ownKeys = Object.getOwnPropertyNames || function (o) {
            var ar = [];
            for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
            return ar;
        };
        return ownKeys(o);
    };
    return function (mod) {
        if (mod && mod.__esModule) return mod;
        var result = {};
        if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
        __setModuleDefault(result, mod);
        return result;
    };
})();
var __metadata = (this && this.__metadata) || function (k, v) {
    if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
var TextractService_1;
Object.defineProperty(exports, "__esModule", { value: true });
exports.TextractService = void 0;
const common_1 = require("@nestjs/common");
const client_textract_1 = require("@aws-sdk/client-textract");
const client_s3_1 = require("@aws-sdk/client-s3");
const fs = __importStar(require("node:fs"));
let TextractService = TextractService_1 = class TextractService {
    constructor() {
        this.logger = new common_1.Logger(TextractService_1.name);
        const awsS3Credentials = {
            S3_AWS_ACCESS_KEY: process.env.S3_AWS_ACCESS_KEY,
            S3_AWS_SECRET_KEY: process.env.S3_AWS_SECRET_KEY,
            S3_AWS_REGION_NAME: process.env.S3_AWS_REGION_NAME
        };
        if (!this.isValidS3Config(awsS3Credentials)) {
            return;
        }
        this.s3Client = new client_s3_1.S3Client({
            region: process.env.S3_AWS_REGION_NAME,
            credentials: {
                accessKeyId: process.env.S3_AWS_ACCESS_KEY,
                secretAccessKey: process.env.S3_AWS_SECRET_KEY,
            },
        });
        this.textractClient = new client_textract_1.TextractClient({
            region: process.env.S3_AWS_REGION_NAME,
            credentials: {
                accessKeyId: process.env.S3_AWS_ACCESS_KEY,
                secretAccessKey: process.env.S3_AWS_SECRET_KEY,
            },
        });
    }
    isValidS3Config(config) {
        return !!config.S3_AWS_ACCESS_KEY && !!config.S3_AWS_SECRET_KEY && !!config.S3_AWS_REGION_NAME;
    }
    async uploadToS3(localPath, bucket, key) {
        const body = fs.createReadStream(localPath);
        await this.s3Client.send(new client_s3_1.PutObjectCommand({
            Bucket: bucket,
            Key: key,
            Body: body,
            ContentType: 'application/pdf',
        }));
    }
    async startTextDetection(bucket, key) {
        const res = await this.textractClient.send(new client_textract_1.StartDocumentTextDetectionCommand({
            DocumentLocation: { S3Object: { Bucket: bucket, Name: key } },
        }));
        if (!res.JobId)
            throw new Error('Failed to start Textract job');
        return res.JobId;
    }
    async getAllPages(jobId) {
        let status = 'IN_PROGRESS';
        while (status === 'IN_PROGRESS') {
            await this.sleep(3000);
            const head = await this.textractClient.send(new client_textract_1.GetDocumentTextDetectionCommand({ JobId: jobId, MaxResults: 1 }));
            status = head.JobStatus || 'IN_PROGRESS';
            if (status === 'FAILED')
                throw new Error('Textract job failed');
            if (status === 'SUCCEEDED')
                break;
        }
        const pages = [];
        let nextToken = undefined;
        do {
            const res = await this.textractClient.send(new client_textract_1.GetDocumentTextDetectionCommand({
                JobId: jobId,
                NextToken: nextToken,
            }));
            pages.push(res);
            nextToken = res.NextToken;
        } while (nextToken);
        return pages;
    }
    sleep(ms) { return new Promise(r => setTimeout(r, ms)); }
    async startDocumentAnalysis(bucket, key, featureTypes = ['TABLES', 'FORMS']) {
        const res = await this.textractClient.send(new client_textract_1.StartDocumentAnalysisCommand({
            DocumentLocation: { S3Object: { Bucket: bucket, Name: key } },
            FeatureTypes: featureTypes,
        }));
        if (!res.JobId)
            throw new Error('Failed to start Textract analysis job');
        return res.JobId;
    }
    async getAllAnalysisBlocks(jobId) {
        let status = 'IN_PROGRESS';
        while (status === 'IN_PROGRESS') {
            await this.sleep(3000);
            const head = await this.textractClient.send(new client_textract_1.GetDocumentAnalysisCommand({ JobId: jobId, MaxResults: 1 }));
            status = head.JobStatus || 'IN_PROGRESS';
            if (status === 'FAILED')
                throw new Error('Textract analysis job failed');
            if (status === 'SUCCEEDED')
                break;
        }
        const blocks = [];
        let nextToken = undefined;
        do {
            const res = await this.textractClient.send(new client_textract_1.GetDocumentAnalysisCommand({
                JobId: jobId,
                NextToken: nextToken,
            }));
            if (res.Blocks?.length)
                blocks.push(...res.Blocks);
            nextToken = res.NextToken;
        } while (nextToken);
        return blocks;
    }
    collatePageWiseTextFromBlocks(blocks) {
        const byPage = new Map();
        for (const b of blocks ?? []) {
            if (b.BlockType === 'LINE' && b.Text) {
                const page = b.Page ?? 1;
                if (!byPage.has(page))
                    byPage.set(page, []);
                byPage.get(page).push(b);
            }
        }
        const pages = Array.from(byPage.keys()).sort((a, b) => a - b);
        const result = {};
        for (const p of pages) {
            const lines = byPage.get(p);
            lines.sort((a, b) => {
                const at = a.Geometry?.BoundingBox?.Top ?? 0;
                const bt = b.Geometry?.BoundingBox?.Top ?? 0;
                if (Math.abs(at - bt) > 0.002)
                    return at - bt;
                const al = a.Geometry?.BoundingBox?.Left ?? 0;
                const bl = b.Geometry?.BoundingBox?.Left ?? 0;
                return al - bl;
            });
            result[`page_${p}`] = lines.map((l) => l.Text).join('\n');
        }
        return result;
    }
    async analyzePdfInS3ToPageWiseText(bucket, key) {
        this.logger.debug(`Starting DocumentAnalysis for s3://${bucket}/${key}`);
        const jobId = await this.startDocumentAnalysis(bucket, key, ['TABLES', 'FORMS']);
        const blocks = await this.getAllAnalysisBlocks(jobId);
        const pageWise = this.collatePageWiseTextFromBlocks(blocks);
        this.logger.debug(`Completed DocumentAnalysis for s3://${bucket}/${key} with ${Object.keys(pageWise).length} pages`);
        return pageWise;
    }
};
exports.TextractService = TextractService;
exports.TextractService = TextractService = TextractService_1 = __decorate([
    (0, common_1.Injectable)(),
    __metadata("design:paramtypes", [])
], TextractService);
//# sourceMappingURL=textract.service.js.map