global without sharing class Batch_FixDocumentToFiles implements Database.Batchable<sObject>{
    // https://github.com/douglascayers/sfdc-convert-documents-to-files/tree/master/src/classes
    private Set<String> folderIds = new Set<String>();//Add by Li Jun 20230703 
    //Add by Gzw 20231030 Start
    public Batch_FixDocumentToFiles(Set<String> folderIds) {
        this.folderIds = folderIds;
    }
    global Database.QueryLocator start(Database.BatchableContext BC) {
        // String queryObject = 'SELECT Id, Name, OwnerId, ParentId, Parent.Name, Parent.Type, Body, CreatedDate, CreatedById FROM Attachment WHERE Parent.Type =:objectApiName AND CreatedDate >=:creStartDate AND CreatedDate <:creEndDate Order by CreatedDate ASC';
        // if(folderIds.size() > 0){
        //     queryObject = 'SELECT Id, Name, OwnerId, ParentId, Parent.Name, Parent.Type, Body, CreatedDate, CreatedById FROM Attachment WHERE ParentId in:parentIds';
        // }

        String sql = 'SELECT Id, Name, DeveloperName,Description, ContentType,Type,Body,Url,Keywords,FolderId, Folder.Name,Folder.DeveloperName,AuthorId,CreatedById, CreatedDate, LastModifiedById, LastModifiedDate  FROM  Document where FolderId in: folderIds ' ;

        if(System.Test.isRunningTest()){
            sql += ' limit 1';
        }

        return Database.getQueryLocator(sql);
    }
    global void execute(Database.BatchableContext BC, List<Document> scope) {
        Set<ID> documentIds = new Set<ID>();
        Set<ID> folderIds = new Set<ID>();
        for ( Document doc : scope ) {
            documentIds.add( doc.Id );
            folderIds.add( doc.FolderId );
        }

        Set<String> libraryNames = new Set<String>();
        Map<ID, Folder> foldersMap = new Map<ID, Folder>([ SELECT Id, Name, DeveloperName FROM Folder WHERE Id IN :folderIds ]);
        for ( Folder f : foldersMap.values() ) {
            // libraryNames.add( buildLibraryDeveloperName( f.Name ) );
            libraryNames.add( f.Name );
        }

        Map<ID, ContentWorkspace> librariesMap = new Map<ID, ContentWorkspace>([ SELECT Id, Name, DeveloperName FROM ContentWorkspace WHERE Name IN :libraryNames ]);
        Map<String, ContentWorkspace> libraryNamesMap = toMapByKeyField( ContentWorkspace.Name, librariesMap.values() );

        // Map<ID, ID> alreadyConvertedDocumentIdsMap = getAlreadyConvertedDocumentIdsMap( documentIds, librariesMap.keySet() );

        // todo add error handling and logging like in ConvertAttachmentsToFilesService.cls
        List<ContentVersion> newFileVersions = new List<ContentVersion>();

        for ( Document doc : scope ) {

            // todo log a skipped conversion result like attachment converter project
            // if ( alreadyConvertedDocumentIdsMap.containsKey( doc.Id ) ) {
            //     System.debug( 'Document has already been converted, skipping... ' + doc );
            //     continue;
            // }

            Folder f = foldersMap.get( doc.FolderId );
            // String libraryName = buildLibraryDeveloperName( f.DeveloperName );
            ContentWorkspace library = libraryNamesMap.get( f.Name );

            newFileVersions.add( new ContentVersion(
                // data fields
                VersionData = doc.Body,
                PathOnClient = '/' + doc.DeveloperName + '.' + doc.Type,
                Title = doc.Name,
                Description = doc.Description,
                FirstPublishLocationId = library.Id,
                TagCsv = doc.Keywords,
                // audit fields
                OwnerId = doc.AuthorId, // system requirement, owner and creator must be the same
                CreatedById = doc.AuthorId,
                CreatedDate = doc.CreatedDate,
                LastModifiedById = doc.LastModifiedById,
                LastModifiedDate = doc.LastModifiedDate
            ));
        }
        insert newFileVersions;

    }

    // private String buildLibraryDeveloperName( String folderDeveloperName ) {
    //     return 'Doc2File_' + folderDeveloperName;
    // }
    // public Map<ID, ID> getAlreadyConvertedDocumentIdsMap( Set<ID> documentIds, Set<ID> libraryIds ) {

    //     // map of old Document ids to new file ids
    //     Map<ID, ID> convertedDocumentIdsMap = new Map<ID, ID>();

    //     for ( List<ContentDocumentLink> links : [
    //         SELECT
    //             ContentDocument.LatestPublishedVersionId,
    //             ContentDocument.LatestPublishedVersion.Original_Record_ID__c
    //         FROM
    //             ContentDocumentLink
    //         WHERE
    //             LinkedEntityId IN :libraryIds
    //             AND
    //             ContentDocument.LatestPublishedVersion.Original_Record_ID__c IN :documentIds
    //     ]) {

    //         for ( ContentDocumentLink link : links ) {

    //             if ( link.ContentDocument != null && link.ContentDocument.LatestPublishedVersion != null ) {

    //                 if ( documentIds.contains( link.ContentDocument.LatestPublishedVersion.Original_Record_ID__c ) ) {
    //                     convertedDocumentIdsMap.put( link.ContentDocument.LatestPublishedVersion.Original_Record_ID__c, link.ContentDocument.LatestPublishedVersionId );
    //                 }

    //             }

    //         }

    //     }

    //     return convertedDocumentIdsMap;
    // }
    public static Map<String, ContentWorkspace> toMapByKeyField( SObjectField mapKeyField, List<ContentWorkspace> records ) {

        Map<String, ContentWorkspace> recordsMap = new Map<String, ContentWorkspace>();

        for ( ContentWorkspace record : records ) {
            String mapKey = String.valueOf( record.get( mapKeyField ) );
            recordsMap.put( mapKey, record );
        }

        return recordsMap;
    }

    global void finish(Database.BatchableContext BC) {

    }
}