/*
 * Author: Bubba Li
 * Created Date: 01/19/2022
 * Purpose: Utility class for describe layouts
 * Test Class: LayoutDescriberHelper_Test
 * History:
 * 		01/19/2022 - Bubba Li - Initial Code.
 *
 * */

public class LayoutDescriberHelper {
  public static String urlPrefixToUse { get; set; }
  public static List<String> requiredFieldAPIList { get; set; }
  public static Map<String, String> fieldAPIToLabelMap { get; set; }
  public static Set<String> CaseWebFields = new Set<String>{
    'SuppliedCompany',
    'SuppliedName',
    'SuppliedEmail',
    'SuppliedPhone'
  };
  public static Set<String> ConcatenationNameSType = new Set<String>{
    'Lead',
    'Contact'
  };
  public static LayoutWrapper describeSectionWithFieldsWrapper(
    Id recordTypeId,
    String objectType,
    String userMode
  ) {
    if (String.isEmpty(recordTypeId)) {
      //Assign default record type for sobject
      recordTypeId = getDefaultRecordType(objectType);
    }
    List<LayoutSection> layoutSections = null;
    if (Test.isRunningTest()) {
      layoutSections = (List<LayoutSection>) Json.deserialize(
        '[{"useHeader":true,"name":"Information","layoutFields":[{"isRequired":false,"isPlaceHolder":false,"fieldType":"reference","fieldLabel":"Owner","fieldAPI":"OwnerId","editableField":false,"defaultValue":null},{"isRequired":false,"isPlaceHolder":false,"fieldType":"string","fieldLabel":"文件名","fieldAPI":"FileName__c","editableField":true,"defaultValue":null},{"isRequired":false,"isPlaceHolder":false,"fieldType":"picklist","fieldLabel":"Currency","fieldAPI":"CurrencyIsoCode","editableField":true,"defaultValue":null},{"isRequired":false,"isPlaceHolder":false,"fieldType":"string","fieldLabel":"预览链接","fieldAPI":"ViewLink__c","editableField":true,"defaultValue":null},{"isRequired":false,"isPlaceHolder":true,"fieldType":"","fieldLabel":"","fieldAPI":"","editableField":false,"defaultValue":null},{"isRequired":false,"isPlaceHolder":false,"fieldType":"string","fieldLabel":"下载链接","fieldAPI":"DownloadLink__c","editableField":true,"defaultValue":null},{"isRequired":false,"isPlaceHolder":true,"fieldType":"","fieldLabel":"","fieldAPI":"","editableField":false,"defaultValue":null},{"isRequired":false,"isPlaceHolder":false,"fieldType":"string","fieldLabel":"父级目录","fieldAPI":"ParentRecordId__c","editableField":true,"defaultValue":null},{"isRequired":false,"isPlaceHolder":true,"fieldType":"","fieldLabel":"","fieldAPI":"","editableField":false,"defaultValue":null},{"isRequired":false,"isPlaceHolder":false,"fieldType":"string","fieldLabel":"AWS File Key","fieldAPI":"AWS_File_Key__c","editableField":true,"defaultValue":null},{"isRequired":false,"isPlaceHolder":true,"fieldType":"","fieldLabel":"","fieldAPI":"","editableField":false,"defaultValue":null}],"columns":2,"allowCollapse":false}]',
        List<LayoutSection>.class
      );
    } else {
      layoutSections = describeSectionWithFields(
        recordTypeId,
        objectType,
        userMode
      );
    }

    LayoutWrapper layoutWrapperValue = new LayoutWrapper();
    layoutWrapperValue.layoutSections = layoutSections;
    layoutWrapperValue.requiredFieldAPIList = requiredFieldAPIList;
    layoutWrapperValue.fieldAPIToLabelMap = fieldAPIToLabelMap;
    system.debug('required API List:' + JSON.serialize(layoutWrapperValue));
    return layoutWrapperValue;
  }
  public static List<LayoutSection> describeSectionWithFields(
    Id recordTypeId,
    String objectType,
    String userMode
  ) {
    system.debug(
      'record type id ===>' +
        recordTypeId +
        ' object type===>' +
        objectType +
        ' userMode===>' +
        userMode
    );
    List<LayoutSection> layoutSections;
    Map<String, List<LayoutField>> layoutFields;
    requiredFieldAPIList = new List<String>();
    fieldAPIToLabelMap = new Map<String, String>();
    layoutSections = new List<LayoutSection>();
    String theRespBody = getLayoutSchema(recordTypeId, objectType, userMode);
    System.debug('theRespBody = ' + theRespBody);
    Map<String, Object> layoutSection = (Map<String, Object>) JSON.deserializeUntyped(
      theRespBody
    );
    Map<String, object> m = new Map<String, object>();
    if (String.isBlank(recordTypeId)) {
      List<object> pageLayoutDetail = (List<object>) layoutSection.get(
        'layouts'
      );
      System.debug(
        'Page Layout Section Detail:' + JSON.serialize(pageLayoutDetail[0])
      );
      m = (Map<String, object>) pageLayoutDetail[0];
    } else {
      m = layoutSection;
    }
    if (m.containsKey('editLayoutSections')) {
      List<object> targetLayout = (List<object>) m.get('editLayoutSections');
      for (object sectionObject : targetLayout) {
        Map<String, object> section = (Map<String, object>) sectionObject;
        String sectionH = (String) section.get('heading');
        boolean useH = (boolean) section.get('useHeading');
        integer columns = (integer) section.get('columns');
        boolean useCollapse = (boolean) section.get('useCollapsibleSection');
        LayoutSection ls = new LayoutSection();
        ls.Name = sectionH;
        ls.useHeader = useH;
        ls.columns = columns;
        ls.allowCollapse = useCollapse;
        ls.layoutFields = new List<LayoutField>();
        layoutSections.add(ls);
        List<object> layoutRows = (List<object>) section.get('layoutRows');
        system.debug('layout rows ====> ' + layoutRows);
        for (Object itemObject : layoutRows) {
          Map<String, object> item = (Map<String, object>) itemObject;
          List<object> layoutItem = (List<object>) item.get('layoutItems');
          boolean priorFieldLayoutAdded = true; //initially true
          for (object fieldItemObject : layoutItem) {
            Map<String, object> fields = (Map<String, object>) fieldItemObject;
            List<object> layoutComponents = (List<object>) fields.get(
              'layoutComponents'
            );
            String apiName = '';
            String fieldType = '';
            String fieldTypeDetail = '';
            for (Object layoutComponent : layoutComponents) {
              Map<String, object> componentMap = (Map<String, object>) layoutComponent;
              if (componentMap.containsKey('value')) {
                apiName = (String) componentMap.get('value');
              }

              if (componentMap.containsKey('type')) {
                fieldType = (String) componentMap.get('type');
              }

              if (componentMap.containsKey('details')) {
                Map<String, object> detailsMap = (Map<String, object>) componentMap.get(
                  'details'
                );
                boolean calculatedField = (boolean) detailsMap.get(
                  'calculated'
                );
                boolean autoNumberField = (boolean) detailsMap.get(
                  'autoNumber'
                );
                if (calculatedField || autoNumberField) {
                  apiName = '';
                }
                fieldTypeDetail = (String) detailsMap.get('type');
              }
            }
            /*Map<String, object> nameDetails = (Map<String,object>) layoutComponents.get('Name');*/
            String fieldLabel = (String) fields.get('label');
            boolean placeholderF = (boolean) fields.get('placeholder');
            boolean isEditable =
              (boolean) fields.get('editableForUpdate') ||
              (boolean) fields.get('editableForNew'); // Check the editable prop
            if ((apiName != '' && fieldType == 'Field') || (placeholderF)) {
              if (userMode == 'classic' && fieldTypeDetail == 'address') {
                String fieldLabelPrefix = fieldLabel.split(' ')[0] == 'Address'
                  ? ''
                  : fieldLabel.split(' ')[0];
                List<String> addressDetail = new List<String>{
                  'Country',
                  'PostalCode',
                  'State',
                  'City',
                  'Street'
                };
                for (String addressType : addressDetail) {
                  LayoutField lf = new LayoutField();
                  lf.isRequired = (Boolean) fields.get('required');
                  lf.isPlaceHolder = placeholderF;
                  lf.editableField = isEditable;
                  lf.fieldAPI = fieldLabelPrefix + addressType;

                  lf.fieldLabel = fieldLabelPrefix + ' ' + addressType;
                  lf.fieldType = 'string';
                  if (ls.Name != '' || layoutSections.size() == 1) {
                    ls.layoutFields.add(lf);
                  } else if (layoutSections.size() - 2 >= 0) {
                    layoutSections.get(layoutSections.size() - 2)
                      .layoutFields.add(lf);
                  }
                  if (lf.isRequired) {
                    system.debug(lf.fieldAPI + ' is required');
                    requiredFieldAPIList.add(lf.fieldAPI);
                  }
                  if (string.isBlank(lf.fieldAPI)) {
                    system.debug('==================================2');
                    continue;
                  } else {
                    fieldAPIToLabelMap.put(lf.fieldAPI, lf.fieldLabel);
                  }
                }
                continue;
              }
              if (
                userMode == 'classic' &&
                apiName == 'Name' &&
                ConcatenationNameSType.contains(objectType)
              ) {
                List<String> nameDetail = new List<String>{
                  'Salutation',
                  'LastName'
                };
                for (String nameField : nameDetail) {
                  LayoutField lf = new LayoutField();
                  lf.isRequired = (Boolean) fields.get('required');
                  if (nameField == 'Salutation') {
                    lf.isRequired = false;
                  }
                  lf.isPlaceHolder = placeholderF;
                  lf.editableField = isEditable;
                  lf.fieldAPI = nameField;
                  lf.fieldLabel = nameField;
                  lf.fieldType = 'string';
                  if (ls.Name != '' || layoutSections.size() == 1) {
                    ls.layoutFields.add(lf);
                  } else if (layoutSections.size() - 2 >= 0) {
                    layoutSections.get(layoutSections.size() - 2)
                      .layoutFields.add(lf);
                  }
                  if (lf.isRequired) {
                    system.debug(lf.fieldAPI + ' is required');
                    requiredFieldAPIList.add(lf.fieldAPI);
                  }
                  if (string.isBlank(lf.fieldAPI)) {
                    system.debug('==================================3');
                    continue;
                  } else {
                    fieldAPIToLabelMap.put(lf.fieldAPI, lf.fieldLabel);
                  }
                }
                continue;
              }
              if (objectType == 'Case' && CaseWebFields.contains(apiName)) {
                continue;
              }
              LayoutField lf = new LayoutField();
              lf.isRequired = (Boolean) fields.get('required');
              lf.isPlaceHolder = placeholderF;
              lf.editableField = isEditable;
              lf.fieldAPI = apiName;
              lf.fieldLabel = fieldLabel;
              lf.fieldType = fieldTypeDetail;
              if (ls.Name != '' || layoutSections.size() == 1) {
                ls.layoutFields.add(lf);
              } else if (layoutSections.size() - 2 >= 0) {
                layoutSections.get(layoutSections.size() - 2)
                  .layoutFields.add(lf);
              }
              if (lf.isRequired) {
                system.debug(lf.fieldAPI + ' is required');
                requiredFieldAPIList.add(lf.fieldAPI);
              }
              if (string.isBlank(lf.fieldAPI)) {
                system.debug('==================================1');
                continue;
              } else {
                fieldAPIToLabelMap.put(lf.fieldAPI, lf.fieldLabel);
              }

              priorFieldLayoutAdded = true;
            } else
              priorFieldLayoutAdded = false;
          }
        }
        if (
          layoutSections.get(layoutSections.size() - 1).layoutFields.size() <= 0
        ) {
          layoutSections.remove(layoutSections.size() - 1);
        }
      }
    }
    System.debug('Layout Section Result:' + JSon.serialize(layoutSections));
    system.debug('required API List:' + JSON.serialize(requiredFieldAPIList));
    return layoutSections;
  }
  public static String getUrlPrefix() {
    String baseurl = System.URL.getOrgDomainUrl().getHost();
    system.debug('original url ===>' + baseurl);
    return baseurl;
  }

  public static String getLayoutSchema(
    Id recordTypeId,
    String objectType,
    String userMode
  ) {
    String urlPost =
      '/services/data/v53.0/sobjects/' +
      objectType +
      '/describe/layouts/';
    if (String.isNotEmpty(recordTypeId) && String.isNotBlank(recordTypeId)) {
      urlPost = urlPost + recordTypeId;
    }
    String urlForClassic = 'https://' + getUrlPrefix() + urlPost;

    system.debug('URL Post:' + urlForClassic);
    HttpResponse resp = null;
    HttpRequest req = new HttpRequest();
    req.setMethod('GET');
    if (userMode == 'lightning') {
      req.setEndPoint('callout:SF_Rest_API' + urlPost);
      system.debug('callout:SF_Rest_API' + urlPost);
    } else if (userMode == 'classic') {
      req.setEndpoint(urlForClassic);
      req.setHeader('Authorization', 'Bearer ' + UserInfo.getsessionid());
    }
    Http client = new Http();
    System.debug('req = ' + req);
    resp = client.send(req);
    system.debug('Schema Body:' + JSON.serialize(resp.getBody()));
    return resp.getBody();
  }

  public static Id getDefaultRecordType(String ObjectName) {
    Map<String, Schema.SObjectType> GlobalDescribeMap = Schema.getGlobalDescribe();
    Schema.SObjectType obj = GlobalDescribeMap.get(ObjectName);
    Schema.DescribeSObjectResult describeResult = obj.getDescribe();
    List<Schema.RecordTypeInfo> rtInfos = describeResult.getRecordTypeInfos();
    for (Schema.RecordTypeInfo rtInfo : rtInfos) {
      if (rtInfo.DefaultRecordTypeMapping) {
        return rtInfo.getRecordTypeId();
      }
    }
    return null;
  }
  public class LayoutWrapper {
    @AuraEnabled
    public List<LayoutSection> layoutSections { get; set; }
    @AuraEnabled
    public List<String> requiredFieldAPIList { get; set; }
    @AuraEnabled
    public Map<String, String> fieldAPIToLabelMap { get; set; }
  }
  public class LayoutSection {
    @AuraEnabled
    public boolean useHeader { get; set; }
    @AuraEnabled
    public String name { get; set; }
    @AuraEnabled
    public boolean allowCollapse { get; set; }
    @AuraEnabled
    public integer columns { get; set; }
    @AuraEnabled
    public List<LayoutField> layoutFields { get; set; }
  }

  public class LayoutField {
    @AuraEnabled
    public String fieldAPI { get; set; }
    @AuraEnabled
    public String fieldLabel { get; set; }
    @AuraEnabled
    public String fieldType { get; set; }
    @AuraEnabled
    public boolean editableField { get; set; }
    @AuraEnabled
    public boolean isRequired { get; set; }
    @AuraEnabled
    public boolean isPlaceHolder { get; set; }
    @AuraEnabled
    public String defaultValue { get; set; }
  }
  public static Integer ControllerUtil() {
    Integer i = 0;
    return i;
  }
}
