import { Component, OnInit } from '@angular/core';
import { FormBuilder, FormGroup } from '@angular/forms';
import { ActivatedRoute, Router } from '@angular/router';
import { TreeNode } from 'primeng/api';
import { CommonFunctionService } from 'src/app/core/common/common-function.service';
import { securityFunctions } from 'src/app/core/common/security';
import { ValidationService } from 'src/app/core/common/validation.service';
import { dtoRoleAccessMapping, dtoRoleMaster, dtoRoleMaster_Root } from 'src/app/core/models/user-role-management/user-role.model';
import { API } from 'src/app/core/services/api.service';

interface Column {
  field: string;
  header: string;
}

@Component({
  selector: 'app-role-add',
  templateUrl: './role-add.component.html',
})

export class RoleAddComponent implements OnInit {

  constructor(
    private router: Router,
    private ApiServ: API,
    public fb: FormBuilder,
    public validationServ: ValidationService,
    public route: ActivatedRoute,
    public commonServ: CommonFunctionService
  ) { }
  files!: TreeNode[];
  selectedNodes: TreeNode[] = []
  HeaderList!: Column[];
  formdata: FormGroup;
  allStaff;
  EditId;
  newMenuList = [];
  RoleAccessMapping = [];
  AllMenuMasterNew;
  roleAccessMappingdata;
  roleType = [
    { value: 'Extranet', label: 'Extranet' }
  ]
  ngOnInit(): void {
    this.ngOnintForm();
    this.ngOinitEditForm();
    this.commonServ.LoginUserData = securityFunctions.getSessionStorage('LoginUserData') ? JSON.parse(securityFunctions.getSessionStorage('LoginUserData')) : null
    this.HeaderList = [
      { field: 'menu_name', header: 'Name' },
      { field: 'View', header: 'View' },
      { field: 'Add', header: 'Add' },
      { field: 'Edit', header: 'Edit' },
      { field: 'Download', header: 'Download' },
      { field: 'Status', header: 'Status' },
    ];
  }
  ngOnintForm() {
    let VData = JSON.parse(securityFunctions.getSessionStorage('VData'));
    console.log(VData);
    this.formdata = this.fb.group({
      role_name: this.validationServ.required,
      reporting_role_id: [''],
      role_description: this.validationServ.required,
      role_type: ['Extranet'],
      extranet_vendor_id: [VData.vendor_name],
      is_active: [false]
    });
    this.formdata.get('role_type').disable();
    this.formdata.get('extranet_vendor_id').disable();
    this.GetAllMenu(false);
  };



  /**
  * @author Abdul razzak
  *  @description Copies and binds menu items based on the provided list.
  * @param {array} list - The list of menu items to copy and bind.
  */
  CopyBindMenu(list) {
    let role_type = '2405170833594899108'
    let aisemenu = [];
    // Sort the list based on sequence
    let FliterList = list.sort(function (a, b) { return a.sequence - b.sequence; });
    // Filter all parent menu items
    let AllParentlist = list.filter(e => (role_type == '2405170833594899108' ? true : e.ui_controller == '') && e.parent_id == '');
    AllParentlist.forEach(e => {
      // Check menu type and push data accordingly
      if (e.menu_type == '2305170833594899101') {
        aisemenu.push(
          {
            "data": {
              "menu_name": e.menu_name,
              "menu_id": e.menu_id,
              "View": false,
              "Add": false,
              "Edit": false,
              "Download": false,
              "Status": false,
            },
            // "parent": null,
            "children": []
          }
        );
      } else {
        aisemenu.push(
          {
            "data": {
              "menu_name": e.menu_name,
              "menu_id": e.menu_id,
              "View": false,
              "Add": false,
              "Edit": false,
              "Status": false,
              "Download": false,
            }
          }
        );
      }
      let index = aisemenu.length - 1;
      // Filter submenu items for the current parent menu
      let submenu = FliterList.filter(s => s.parent_id == e.menu_id);
      // If submenu items exist, recursively copy submenu
      if (submenu.length > 0) {
        this.CopysubMenu(aisemenu[index], submenu, FliterList);
      }
    });
    this.newMenuList = aisemenu;
  }
  /**
   * @author Abdul Razzak
   * @description Copies submenu items and binds them to the provided menu.
   * @param {object} menu - The menu object to which submenu items are to be copied and bound.
   * @param {array} FilterSubmenu - The list of submenu items to copy and bind.
   * @param {array} Allmenu - The list of all menu items.
   */
  CopysubMenu(menu, FilterSubmenu, Allmenu) {
    if (FilterSubmenu.length > 0) {
      FilterSubmenu.forEach(e => {
        // Check menu type and push data accordingly
        if (e.menu_type == '2305170833594899101') {
          menu.children.push(
            {
              "data": {
                "menu_name": e.menu_name,
                "menu_id": e.menu_id,
                "View": false,
                "Add": false,
                "Edit": false,
                "Download": false,
                "Status": false,
              },
              "children": []
            }
          );
        } else {
          menu.children.push(
            {
              "data": {
                "menu_name": e.menu_name,
                "menu_id": e.menu_id,
                "View": false,
                "Add": false,
                "Edit": false,
                "Download": false,
                "Status": false,
              }
            }
          );
        }
        let index = menu.children.length - 1;
        // Filter submenu items for the current submenu
        let submenu = Allmenu.filter(a => a.parent_id == e.menu_id);
        // If submenu items exist, recursively copy submenu
        if (submenu.length > 0) {
          this.CopysubMenu(menu.children[index], submenu, Allmenu);
        }
      })
    }
  }
  /**
   * @author Abdul Razzak
   * @description Checks data against provided list and updates selectedNodes accordingly.
   * @param data - The data object to be checked.
   * @param list - The list object containing flags for Add, Edit, and View.
   * @param name  - The name of the property to be updated in data.node.parent.
   */
  checkData(data, list, name) {
    // If View permission is not granted, disable Add and Edit
    if (!list.View) {
      list.Add = false
      list.Edit = false
      list.Status = false
      list.Download = false
    };
    // If Add, Edit, and View permissions are granted, add data.node to selectedNodes
    if (list.Add && list.Edit && list.View && list.Download && list.Status) {
      this.selectedNodes = [...this.selectedNodes, data.node];
    }
    // If any of the permissions are not granted, remove data.node from selectedNodes
    else if (!list.Add || !list.Edit || !list.View || list.Status || list.Download) {
      this.selectedNodes = this.selectedNodes.filter(selectedNode => selectedNode !== data.node);
    };
    // Update parent data property if any of the permissions are granted
    if (list.Add || list.Edit || list.View || list.Status || list.Download) {
      if (data.node.parent) {
        data.node.parent.data[name] = true;
      }
    };
    // Recursively check children nodes
    if (data.node.children) this.CheeckcolumLevel(data, name, list[name]);
    // Check if any parent or child nodes are checked
    let parentdata = this.getParentNode(data.node);
    this.isAnyParentOrChildChecked(parentdata);
  };


  /**
   * @author Abdul Razzak
   * @description   Function to check if any child node has properties (View, Edit, Add) set to true
   * @param node 
   * @returns boolen
   */
  isAnyChildChecked(node): any {
    // If node has no children, check its own properties
    if (!node.children || node.children.length === 0) {
      return node.data.View || node.data.Edit || node.data.Add || node.data.Download || node.data.Status;
    }
    // If node has children, check each child recursively
    for (const child of node.children) {
      if (this.isAnyChildChecked(child)) {
        return true; // If any child or its descendants are checked, return true
      }
    }
    // If none of the children or their descendants are checked, return false
    return false;
  }

  /**
  * @author Abdul Razzak
  * @description Checks if any parent or child nodes are checked and updates their partialSelected property accordingly.
  * @param {object} node - The node to check for parent or child checked status.
  */
  isAnyParentOrChildChecked(node) {
    if (!node) return;
    // Check if any child nodes are checked
    let isChildChecked = this.isAnyChildChecked(node);
    // Check if all children are checked
    let parentlevel = this.areAllChildrenChecked(node);
    // Set partialSelected property for the current node based on child checked status
    if (node.children && (isChildChecked) && !parentlevel) {
      node.partialSelected = true;
      this.selectedNodes = this.selectedNodes.filter(selectedNode => selectedNode !== node);
    } else {
      if (node.children && (isChildChecked)) {
        node.partialSelected = true;
      } else node.partialSelected = false
      if (parentlevel) {
        node.partialSelected = false
        this.selectedNodes = [...this.selectedNodes, node];
      }
    }
    // Recursively set partialSelected property for all children
    if (node.children) {
      for (const child of node.children) {
        this.isAnyParentOrChildChecked(child);
      }
    }
  }

  /**
   * @author Abdul Razzak
   * @description Checks the parent level permissions for a given node and its children.
   * @param {object} data - The data object containing the node whose permissions are to be checked.
   * @param {boolean} val - The value to set for Add, Edit, and View permissions.
   */
  CheckParentLevel(data, val) {
    if (data.node) {
      // Set Add, Edit, and View permissions for the current node
      data.node.data.Add = val;
      data.node.data.Edit = val;
      data.node.data.View = val;
      data.node.data.Status = val;
      data.node.data.Download = val;
      // Recursively check child level permissions if children exist
      data.node['children'] ? this.checkchildLevel(data.node['children'], val) : '';
    };
  };
  /**
   * @author Abdul Razzak
   * @description Checks the child level permissions for a given submenu and its children.
   * @param {array} submenu - The array containing submenu items whose permissions are to be checked.
   * @param {boolean} val - The value to set for Add, Edit, and View permissions.
   */
  checkchildLevel(submenu, val) {
    submenu.forEach(e => {
      // Set Add, Edit, and View permissions for each submenu item
      e.data.Add = val;
      e.data.Edit = val;
      e.data.Status = val;
      e.data.Download = val;
      e.data.View = val;
      if (e['children']) {
        // Recursively check child level permissions if children exist
        this.checkchildLevel(e['children'], val);
      }
    })
  };


  /**
   * @author Abdul Razzak
   * @description Checks the column level permissions for a given node and its children.
   * @param {object} data - The data object containing the node whose permissions are to be checked.
   * @param {string} name - The name of the permission to be checked (e.g., 'View', 'Add', 'Edit').
   * @param {boolean} val - The value to set for the specified permission.
   */
  CheeckcolumLevel(data, name, val) {
    if (data.node) {
      // Set Add and Edit permissions to false if View permission is set to false
      name == 'View' && !val ? data.node.data.Add = val : '';
      name == 'View' && !val ? data.node.data.Edit = val : '';
      name == 'View' && !val ? data.node.data.Status = val : '';
      name == 'View' && !val ? data.node.data.Download = val : '';
      // Set the specified permission for the current node
      data.node.data[name] = val;
      // Recursively set column level permissions for children if they exist
      data.node['children'] ? this.SetchildcolumLevel(data.node['children'], name, val) : '';
    }
  };

  /**
     * @author Abdul Razzak
     * @description Sets the column level permissions for children submenu items.
     * @param {array} submenu - The array containing submenu items whose permissions are to be set.
     * @param {string} name - The name of the permission to be set (e.g., 'View', 'Add', 'Edit').
     * @param {boolean} val - The value to set for the specified permission.
   */
  SetchildcolumLevel(submenu, name, val) {
    submenu.forEach(e => {
      // Set Add and Edit permissions to false if View permission is set to false
      name == 'View' && !val ? e.data.Add = val : '';
      name == 'View' && !val ? e.data.Edit = val : '';
      name == 'View' && !val ? e.data.Status = val : '';
      name == 'View' && !val ? e.data.Download = val : '';
      // Set the specified permission for the submenu item
      if (name != 'View') e.data.View ? e.data[name] = val : '';
      else e.data[name] = val;
      // Update selectedNodes based on permissions
      if (e.data.Add && e.data.Edit && e.data.View && e.data.Status && e.data.Download) {
        this.selectedNodes = [...this.selectedNodes, e];
      } else if (!e.data.Add || !e.data.Edit || !e.data.View || e.data.Status || e.data.Download) {
        this.selectedNodes = this.selectedNodes.filter(selectedNode => selectedNode !== e);
      };
      // Recursively set column level permissions for children if they exist
      if (e['children']) {
        this.SetchildcolumLevel(e['children'], name, val);
      }
    })

  };

  /**
   * @author Abdul Razzak
   * @description Checks a TreeNode and adds it to the selectedNodes if it's not already included.
   * @param {TreeNode} node - The TreeNode to be checked and added.
   */
  checkNode(node: TreeNode) {
    if (!this.selectedNodes.includes(node)) {
      this.selectedNodes = [...this.selectedNodes, node];
    }
  };
  /**
   * @author  Abdul Razzak
   * @description Finds a TreeNode with the specified menu ID within an array of TreeNode objects.
   * @param {TreeNode[]} nodes - The array of TreeNode objects to search.
   * @param {number} menuId - The menu ID to search for.
   * @returns {TreeNode | null} - The matching TreeNode if found, otherwise null.
   */
  findMatchingNode(nodes: TreeNode[], menuId: number): TreeNode | null {
    for (const node of nodes) {
      if (node.data.menu_id === menuId) {
        return node;
      }
      if (node.children) {
        const matchingChild = this.findMatchingNode(node.children, menuId);
        if (matchingChild) {
          return matchingChild;
        }
      }
    }

    return null;
  };

  /**
   * @author Abdul Razzak
   * @description Checks if all children and their descendants of a TreeNode are checked.
   * @param {TreeNode} node - The TreeNode to check.
   * @returns {boolean} - True if all children and their descendants are checked, otherwise false.
   */
  areAllChildrenChecked(node): boolean {
    if (!node.children || node.children.length === 0) {
      // If node has no children, return true if node's permissions are all true
      return node.data.View && node.data.Edit && node.data.Add && node.data.Download && node.data.Status;
    }
    for (const child of node.children) {
      // Recursively check all children and their descendants
      if (!this.areAllChildrenChecked(child)) {
        return false; // If any child or its descendants are not checked, return false
      }
    }
    return true; // All children and their descendants are checked
  };

  /**
   * @author Abdul Razzak
   * @description Recursively finds the topmost parent node of the given node.
   * @param  node - The node whose parent is to be found.
   * @returns  - The topmost parent node.
   */
  getParentNode(node) {
    // If node has a parent, recursively call getParentNode with the parent node
    if (node.parent) {
      return this.getParentNode(node.parent); // Return the result of the recursive call
    } else {
      // If node has no parent, return the node itself (topmost parent)
      return node;
    }
  };

  ngOinitEditForm() {
    this.EditId = this.route.snapshot.paramMap.get('RuleId') as string;
    if (this.EditId) {
      this.ApiServ.AdminGet("/RoleMaster/GetAllRoleByRoleId_Root/" + this.EditId).subscribe((res) => {
        if (res.respObj) {
          this.setValue(res.respObj);
        }
      });
    }
  };

  /**
   * @author Abdul Razzak
   * @description Sets values for form fields based on the provided data.
   * @param {object} data - The data object containing roleMaster and roleAccessMapping information.
  */
  setValue(data) {
    if (data.roleMaster) {
      this.formdata.get('role_name').setValue(data.roleMaster['role_name']);
      this.formdata.get('role_description').setValue(data.roleMaster['role_description']);
      this.formdata.get('role_type').setValue(data.roleMaster['role_type']);
      this.formdata.get('is_active').setValue(data.roleMaster['is_active']);
      this.GetAllMenu(true);
    }
    this.roleAccessMappingdata = data.roleAccessMapping;
  };
  /**
   * @author Abdul Razzak
   * @description Sets role access mapping based on the provided roleAccessMappingdata.
   */
  setroleAccessMapping() {
    if (this.roleAccessMappingdata) {
      if (this.roleAccessMappingdata.length > 0) {
        this.roleAccessMappingdata.forEach(r => {
          let json = JSON.parse(r.permission)
          if (json.View) {
            let Obj = this.findMatchingNode(this.newMenuList, r.menu_id);
            if (Obj) {
              Obj.data['Add'] = json.Add;
              Obj.data['Edit'] = json.Edit;
              Obj.data['View'] = json.View;
              Obj.data['Status'] = json.Status;
              Obj.data['Download'] = json.Download;
              if (Obj.data['Add'] && Obj.data['Edit'] && Obj.data['View'] && Obj.data['Status'] && Obj.data['Download']) {
                this.selectedNodes = [...this.selectedNodes, Obj];
              } else if (!Obj.data['Add'] || !Obj.data['Edit'] || !Obj.data['View'] || Obj.data['Status'] || Obj.data['Download']) {
                this.selectedNodes = this.selectedNodes.filter(selectedNode => selectedNode !== Obj);
              };
            }
          }
        })
        this.newMenuList.forEach(e => {
          let parentdata = this.getParentNode(e);
          this.isAnyParentOrChildChecked(parentdata);
        })
      }
    }
  };
  save() {
    if (this.formdata.valid) {
      let val = this.formdata.getRawValue();
      let postdata = new dtoRoleMaster()
      postdata.reporting_role_id = '';
      postdata.role_description = val.role_description;
      postdata.role_name = val.role_name;
      postdata.role_type = val.role_type;
      postdata.extranet_vendor_id = this.commonServ.LoginUserData.extranet_vendor_id;
      postdata.is_active = val.is_active;
      postdata.role_id = this.EditId ? this.EditId : '';
      this.RoleAccessMapping = [];
      if (this.newMenuList.length > 0) {
        this.newMenuList.forEach(MainObj => {
          let e = MainObj.data
          let PostdataArr = new dtoRoleAccessMapping();
          PostdataArr.project_id = '';
          PostdataArr.menu_id = e.menu_id;
          PostdataArr.is_active = val.is_active;
          let obj = {}
          obj['View'] = e.View;
          obj['Add'] = e.Add;
          obj['Delete'] = e.Delete;
          obj['Edit'] = e.Edit;
          obj['Status'] = e.Status;
          obj['Download'] = e.Download;
          PostdataArr.permission = JSON.stringify(obj);
          this.RoleAccessMapping.push(PostdataArr)
          MainObj['children'] ? this.AddSubmenu(MainObj['children']) : '';
        })
      }
      let postObj = new dtoRoleMaster_Root();
      postObj.roleMaster = postdata;
      postObj.roleAccessMapping = this.RoleAccessMapping;
      let url = this.EditId ? "UpdateRoleMaster_Root" : "SaveRoleMaster_Root";
      this.ApiServ.AdminPost("/RoleMaster/"+ url, postObj).subscribe(
        (res) => {
          if (res) {
            res["resp"] ? this.showToastrMsg(res) : this.showToastrMsg(res);
          }
        }
      );
    } else {
      this.validationServ.showValidationsMsg(this.formdata);
    }
  };

  /**
   * @author Abdul Razzak
   * @description Adds submenu items to the RoleAccessMapping array based on the provided data.
   * @param {TreeNode[]} data - The array of TreeNode objects representing submenu items.
   */
  AddSubmenu(data) {
    let val = this.formdata.getRawValue();
    data.forEach(MainObj => {
      let e = MainObj.data
      let PostdataArr = new dtoRoleAccessMapping();
      PostdataArr.project_id = '';
      PostdataArr.menu_id = e.menu_id;
      PostdataArr.is_active = val.is_active;
      let obj = {}
      obj['View'] = e.View;
      obj['Add'] = e.Add;
      obj['Delete'] = e.Delete;
      obj['Edit'] = e.Edit;
      obj['Status'] = e.Status;
      obj['Download'] = e.Download;
      PostdataArr.permission = JSON.stringify(obj);
      this.RoleAccessMapping.push(PostdataArr);
      MainObj['children'] ? this.AddSubmenu(MainObj.children) : ''
    });
  };

  showToastrMsg(respData) {
    this.commonServ.showMessage(respData['resp'], respData['respMsg']);
    setTimeout(() => {
      respData["resp"] ? this.BackButton() : "";
    }, 500);
  };

  /**
   * Navigates to the view page of Role Management when the back button is clicked.
   */
  BackButton() {
    this.router.navigateByUrl("UserConfiguration/RoleManagement/Search");
  };


  /**
   * @author Abdul Razzak
   * @description Retrieves all menu items based on the project ID and affiliate ID (if present),
   * and updates the menu list accordingly. Optionally triggers role access mapping setting.
   * @param {boolean} check - Indicates whether to set role access mapping after updating the menu list.
   */
  GetAllMenu(check) {
    this.commonServ.LoginUserData = securityFunctions.getSessionStorage('LoginUserData') ? JSON.parse(securityFunctions.getSessionStorage('LoginUserData')) : null
    if (this.commonServ.LoginUserData.extranet_vendor_id) {
      this.ApiServ.AdminGet(`/MenuMasterNew/GetMenuMasterNewsByOrgId/2405170833594899108`).subscribe(res => {
        if (res) {
          this.AllMenuMasterNew = res;
          this.CopyBindMenu(this.AllMenuMasterNew);
          if (check) {
            this.setroleAccessMapping()
          }
        }
      })
    } else {
      this.newMenuList = [];
    }
  };

  backClick() {
    this.router.navigateByUrl("/UserConfiguration/RoleManagement/Search");
   }

}
