const pageSize = 10;
let ipAssets = [], currentIpPage = 1;

document.addEventListener("DOMContentLoaded", async () => {
    // 初始加载
    loadIpAssets();
    loadUrlAssets();
});

// 页面卸载时断开连接
window.addEventListener("beforeunload", function() {
  ipAssets = [];
  urlAssets = [];
  currentIpPage = 1;
});



//---------------------------IP-Assets Tab----------------------------
async function exportIpAssets(){
      // 构造 CSV 文本
      let rows = [
        ['序号', '资产IP', '所属用户', '风险', '最新检测时间','端口数','关联域名'].map(fmtCell).join(',')
      ];
      ipAssets.forEach((row, i) => {
        rows.push([
          (i + 1).toString(),
          row[0].toString(),
          row[1] || '',
          row[2] || '',
          row[3] || '',
          row[4] || '',
          row[5] || '',
        ].map(fmtCell).join(','));
      });

    const csv = '\uFEFF' + rows.join('\r\n');  // 加 BOM

    downloadCSV(csv, `ip_assets.csv`);
}

async function loadIpAssets(page=1) {
    const IP = document.getElementById("ipFilter").value.trim();
    const user = document.getElementById("userFilter").value.trim();
    const safe_rank = document.getElementById("riskFilter").value;
    try {
        const data = await postJSON("/api/assets/getipassets",{IP,user,safe_rank})
        ipAssets = data.ip_assets || [];
        renderIpTable(page);   //刷新表格
      } catch (error) {
        console.error("查询任务记录出错:", error);
        alert("查询失败!");
      }
}

//刷新IP-assets表格
function renderIpTable(page) {
  currentIpPage = page;    //查询数据了,从第一页显示
  const start = (currentIpPage-1)*pageSize;
  const slice = ipAssets.slice(start, start+pageSize);
  const tbody = document.querySelector('#ipTable tbody');
  tbody.innerHTML = '';
  for (let i=0; i<slice.length; i++) {
    const item = slice[i];
    const tr = document.createElement('tr');
    tr.innerHTML = `
      <td>${start+i+1}</td>
      <td>${item[0]}</td>
      <td>${item[1]}</td>
      <td>${item[2]}</td>
      <td>${item[3]}</td>
      <td>${item[4]}</td>
      <td>${item[5]}</td>
      <td>
        <button class="btn btn-sm btn-info asset-op-btn" onclick="showBasicInfo('${item[0]}','${item[1]}','${item[2]}','${item[3]}')">基本信息</button>
        <button class="btn btn-sm btn-info asset-op-btn" data-ip="${item[0]}" onclick="showPortData('${item[0]}')">端口数据</button>
        <button class="btn btn-sm btn-info asset-op-btn" onclick="showVulData('${item[0]}')">漏洞数据</button>
        <button class="btn btn-sm btn-info asset-op-btn" onclick="showRelatedDomain('${item[0]}')">关联域名</button>
        <button class="btn btn-sm btn-danger asset-op-btn" onclick="confirmDeleteIp('${item[0]}')">删除</button>
      </td>
    `;
    tbody.appendChild(tr);
  }
  // 补足空行
  for (let i=slice.length; i<pageSize; i++) {
    const tr = document.createElement('tr');
    tr.innerHTML = '<td colspan="9">&nbsp;</td>';
    tbody.appendChild(tr);
  }
  // 更新分页
  document.getElementById('ipPrev').dataset.page = currentIpPage>1?currentIpPage-1:1;
  document.getElementById('ipNext').dataset.page = ipAssets.length>currentIpPage*pageSize?currentIpPage+1:currentIpPage;
}

// 分页按钮
document.getElementById('ipPrev').addEventListener('click', e => { e.preventDefault(); renderIpTable(+e.target.dataset.page); });
document.getElementById('ipNext').addEventListener('click', e => { e.preventDefault(); renderIpTable(+e.target.dataset.page); });
// 查询
document.getElementById('ipSearchBtn').addEventListener('click', () => loadIpAssets(1));
//导出
document.getElementById("ipExportBtn").addEventListener('click',() => exportIpAssets())

//*********基本信息modal***********
const ipModalEl       = document.getElementById('ipBasicInfoModal');
const ownerDrawerEl   = document.getElementById('ownerDrawer');
const infoIpEl        = document.getElementById('info_ip');
const infoRiskEl      = document.getElementById('info_risk');
const infoOwnerEl     = document.getElementById('info_owner');
const infoScanTimeEl  = document.getElementById('info_scanTime');
const contactNameEl   = document.getElementById('contactName');
const contactPhoneEl  = document.getElementById('contactPhone');
const btnChooseOwner  = document.getElementById('btnChooseOwner');
const btnSaveIpInfo   = document.getElementById('saveIpInfo');
const ownerSearchEl   = document.getElementById('ownerSearchKeyword');
const btnSearchOwner  = document.getElementById('btnSearchOwner');
const btnDelOwner     = document.getElementById("btnDelOwner");
const ownerTableBody  = document.getElementById('ownerTableBody');
const ipinofModal = new bootstrap.Modal(ipModalEl);
const ownerDrawer = new bootstrap.Offcanvas(ownerDrawerEl);
let selectedOwnerId = null;            // 当前选定的 user_id
let owner_list = null;                 // 资产所属单位列表
let curModal;                           //选择所属用户界面的共享参数,用于区分时IP窗口进入,还是URL窗口进入
// 5. Drawer 操作
btnChooseOwner.addEventListener('click', () => {
    curModal = "ip"
    ownerDrawer.show();
    loadOwners(ownerSearchEl.value.trim());
});

//删除所属用户的关系
btnDelOwner.addEventListener('click',() =>{
    selectedOwnerId = null;
    infoOwnerEl.textContent = '';
    contactNameEl.textContent = '';
    contactPhoneEl.textContent = '';
});

//选择一个所属用户
ownerTableBody.addEventListener('click', e => {
  const btn = e.target.closest('button[data-id]');
  if (!btn) return;
  selectedOwnerId = btn.dataset.id;
  if(curModal === "ip"){
     infoOwnerEl.textContent = btn.dataset.name;
  contactNameEl.textContent = btn.dataset.telluname;
  contactPhoneEl.textContent = btn.dataset.tellnum;
  }else{
      ownerEl.textContent = btn.dataset.name;
    contactEl.textContent = btn.dataset.telluname;
    tellEl.textContent = btn.dataset.tellnum;
  }
  ownerDrawer.hide();
});

//5.1--搜索按钮
btnSearchOwner.addEventListener('click', () => loadOwners(ownerSearchEl.value.trim()));

async function loadOwners(keyword='') {
    try {
      const res  = await fetch('/api/assets/getassetsuser', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ keyword })
      });
      owner_list = (await res.json()).user_list;
      ownerTableBody.innerHTML = '';
        //ID,uname,tellnum,tell_username
      owner_list.forEach((u, idx) => {
        const tr = document.createElement('tr');
        tr.innerHTML = `
          <td>${idx + 1}</td>
          <td>${u[1]}</td>
          <td><button class="btn btn-link p-0" data-id="${u[0]}" data-name="${u[1]}"
                        data-tellnum="${u[2]}" data-telluname="${u[3]}">选择</button></td>`;
        ownerTableBody.appendChild(tr);
      });
    } catch (e) { showToast('加载用户列表失败', 'danger'); }
}

// 6. 保存修改
btnSaveIpInfo.addEventListener('click', async () => saveIpInfo());

// 7. hidden 清理
ipModalEl.addEventListener('hidden.bs.modal', () => {
    selectedOwnerId = null;
    document.getElementById('ipInfoForm').reset();
    ['info_ip','info_risk','info_owner','info_scanTime'].forEach(id => {
      document.getElementById(id).textContent = '';
    });
    ownerDrawer.hide();
});

//显示基本信息
async function showBasicInfo(IP,owner,risk_rank,update_time) {
    try {
        infoIpEl.textContent       = IP;
        infoRiskEl.textContent     = risk_rank;
        infoOwnerEl.textContent    = owner;
        infoScanTimeEl.textContent = update_time;
        if(owner == 'null'){
            contactNameEl.textContent        = '';
            contactPhoneEl.textContent       = '';
        }
        else { //au.ID,au.tellnum,au.tell_username
            try{
                const data = await postJSON('/api/assets/getipinfo', {IP});
                ip_info = data.ip_info;
            } catch (error) {
                ip_info = []
                console.error("获取IP资产基本信息失败!",error);
            }
            contactNameEl.textContent        = ip_info[2] || '';
            contactPhoneEl.textContent       = ip_info[1] || '';
            selectedOwnerId            = ip_info[0] || 0;
        }
        ipinofModal.show();
    } catch (e) {
        showToast(`请求基本信息失败:${e.message}`, 'danger');
    }
}

//保存所属用户的基本信息
async function saveIpInfo(){
  const payload = {
    cur_ip: infoIpEl.textContent,
    owner_id: selectedOwnerId
  };
  try {
    const ret = await postJSON('/api/assets/updateipinfo', payload);
    bsuccess = ret.bsuccess;
    if (bsuccess) {
      showToast('修改成功', 'success');
      //bootstrap.Modal.getInstance(ipModalEl).hide();
        loadIpAssets(currentIpPage) //刷新整个数据。。
    } else {
      showToast(ret.message || '修改失败', 'danger');
    }
  } catch (e) {
    showToast(`提交失败:${e.message}`, 'danger');
  }
}

//***********端口数据**************
const latestTableBody   = document.querySelector('#latestPortTable tbody');
const latestPrevBtn     = document.getElementById('latestPrev');
const latestNextBtn     = document.getElementById('latestNext');

const historyTableHead  = document.querySelector('#historyPortTable thead');
const historyTableBody  = document.querySelector('#historyPortTable tbody');
const hisPortPrevBtn     = document.getElementById('hisPortPrev');
const hisPortNextBtn     = document.getElementById('hisPortNext');

const portModalEl = document.getElementById('portDataModal');
const portModal = new bootstrap.Modal(portModalEl);
let latestData = [], historyData = {};
let latestPage = 1, latestPageSize = 10;

async  function showPortData(IP){
    // 把 IP 暂存到 modal 元素上
    portModalEl.dataset.ip = IP;
    portModal.show()
}

  // 打开 Modal 时加载
  portModalEl.addEventListener('show.bs.modal', async ev => {
    const ip = portModalEl.dataset.ip;
    if (!ip) return;

    // 1. 拉最新数据
    try{
        const latestJson = await postJSON('/api/assets/getportlatest', {ip});
        latestData = latestJson.port_data || [];
    } catch (error) {
        console.error("拉取最新的端口数据失败!",error)
    }
    renderLatest();

    // 2. 拉历史数据
    try{
      const histJson = await postJSON('/api/assets/getporthistory', {ip});
      historyData = histJson || [];
    } catch (error) {
        console.error("拉取历史的端口数据失败!",error)
    }
    renderHistory();
  });

  // 渲染最新数据和分页
  function renderLatest() {
    latestTableBody.innerHTML = '';
    const start = (latestPage-1)*latestPageSize;
    const slice = latestData.slice(start, start+latestPageSize);
    slice.forEach((row,i) => {
      const tr = document.createElement('tr');
      tr.innerHTML = `
        <td>${start+i+1}</td>
        <td>${row[0]}</td>
        <td>${row[1]}</td>
        <td>${row[2]}</td>
        <td>${row[3]}</td>
      `;
      latestTableBody.appendChild(tr);
    });
    // 补空行
    for (let i = slice.length; i < latestPageSize; i++) {
      const tr = document.createElement('tr');
      tr.innerHTML = '<td colspan="5">&nbsp;</td>';
      latestTableBody.appendChild(tr);
    }
    // 更新按钮
    latestPrevBtn.dataset.page = latestPage>1?latestPage-1:1;
    latestNextBtn.dataset.page = latestData.length>latestPage*latestPageSize?latestPage+1:latestPage;
  }
  latestPrevBtn.addEventListener('click', e => {
    e.preventDefault();
    latestPage = +e.target.dataset.page;
    renderLatest();
  });
  latestNextBtn.addEventListener('click', e => {
    e.preventDefault();
    latestPage = +e.target.dataset.page;
    renderLatest();
  });

  // 渲染历史数据
  function renderHistory() {
    historyTableHead.innerHTML = '';
    historyTableBody.innerHTML = '';

    // 1) 表头:批次时间
      const th = document.createElement('tr');
      th.innerHTML = `<th style="width:80px">端口 / 时间</th>` +
        historyData.times.map(t => `<th style="width:100px">${t}</th>`).join('');
      historyTableHead.appendChild(th);
      imaxcount = historyData.times.length;

      // 2) 每个端口一行
      Object.entries(historyData.entries).forEach(([port, info]) => {
          const tr = document.createElement('tr');
          // 第一列:端口号
          let rowHtml = `<td>${port}</td>`;
          // 后面的各批次状态i
          let idx = 0;
          for(let i=1;i <= imaxcount;i++){
              if(info.scancount[idx] == i){
                  // 若 changed 为 1,则背景淡红
                  const bg = info.changed[idx] ? 'background-color:#ffecec;' : '';
                  // 鼠标移上时显示服务/版本/协议
                  const title =
                    `服务: ${info.service[idx]}\n` +
                    `版本: ${info.version[idx]}`;
                  rowHtml += `<td style="${bg}" title="${title}">${info.statuses[idx]}</td>`;
                  idx ++;
              } else {//序号不对则加个空单元格
                  rowHtml += `<td style="background-color: lightsteelblue"></td>`;
              }
          }
          tr.innerHTML = rowHtml;
          historyTableBody.appendChild(tr);
      });
  }

  // 导出按钮
  document.getElementById('exportPortData').onclick = () => {
    // 找到当前激活的 tab-pane
    const activePane = document.querySelector('#portTabContent .tab-pane.active').id;

    if (activePane === 'latestPane') {
      // 导出最新数据:可以直接把 latestData 当前页 slice 成 CSV
      exportLatest();
    } else {
      // 导出历史数据
      exportHistory();
    }
  };

  function exportLatest() {
    // 构造 CSV 文本
      let rows = [
        ['序号', '端口号', '服务', '版本号', '端口状态'].map(fmtCell).join(',')
      ];
      latestData.forEach((row, i) => {
        rows.push([
          (i + 1).toString(),
          row[0].toString(),
          row[1] || '',
          row[2] || '',
          row[3] || ''
        ].map(fmtCell).join(','));
      });

    const csv = '\uFEFF' + rows.join('\r\n');  // 加 BOM

    downloadCSV(csv, `latest_ports_${portModalEl.dataset.ip}.csv`);
  }

  function exportHistory() {
    const thead = historyTableHead.querySelector('tr');
    const tbody = historyTableBody.querySelectorAll('tr');

    // 1. 构造表头 CSV
    const headerCells = Array.from(thead.children).map(th => fmtCell(th.textContent.trim()));
    const headerLine = headerCells.join(',');

    // 2. 构造表体 CSV
    const bodyLines = Array.from(tbody).map(tr => {
        const cells = Array.from(tr.children).map(td => fmtCell(td.textContent.trim()));
        return cells.join(',');
    });

    // 3. 拼接,加 BOM
    const csvContent = '\uFEFF' + [headerLine, ...bodyLines].join('\r\n');

    // 4. 发起下载
    downloadCSV(csvContent, `history_ports_${portModalEl.dataset.ip}.csv`);

  }

//***********漏洞数据**************
const vulModalEl = document.getElementById('vulDataModal');
const nodeNameEl = document.getElementById("vulNodeName");
const vulTypeEl = document.getElementById("vulType");
const vulLevelEl = document.getElementById("vulLevel");

const vulModal = new bootstrap.Modal(vulModalEl);
let allVuls = [];
let currentVulPage = 1;
async function showVulData(IP){
    vulModalEl.dataset.ip = IP;
    nodeNameEl.value = "";
    vulTypeEl.value = "";
    vulLevelEl.value = "";
    vulModal.show();
}
vulModalEl.addEventListener('show.bs.modal',async ev =>{
    searchVulnerabilities();
});

async function searchVulnerabilities(page = 1) {
    const ip = vulModalEl.dataset.ip;
    if (!ip) return;
    const nodeName = nodeNameEl.value.trim();
    const vulType = vulTypeEl.value.trim();
    const vulLevel = vulLevelEl.value;
    //拉取该IP最新完成检测的漏洞数据
    try{
        const vuldata = await postJSON('/api/assets/getvuldata', {ip,nodeName,vulType,vulLevel});
        allVuls = vuldata.vuls || [];
        console.log("拉取漏洞数据")
    } catch (error) {
        console.error("拉取漏洞数据失败!",error)
    }
    renderVulPage(1);
}

function renderVulPage(page) {
  currentVulPage = page;
  const start = (page - 1) * pageSize;
  const end = start + pageSize;
  const pageData = allVuls.slice(start, end);

  const tbody = document.querySelector("#vulTable tbody");
  renderTableRows(tbody, pageData);
  document.getElementById("vulTable").scrollIntoView({ behavior: "smooth" });

  // 更新分页按钮
  document.getElementById("vulPrev").dataset.page = page > 1 ? page - 1 : 1;
  document.getElementById("vulNext").dataset.page = (end < allVuls.length) ? page + 1 : page;
}

//使用该函数,需要数据的顺序与表格列的数据一致
function renderTableRows(tbody, rowsData) {
  tbody.innerHTML = "";
  // 遍历数据行,生成 <tr>
  rowsData.forEach((row, index) => {
    const tr = document.createElement("tr");
    // 这里假设 row 为对象,包含各个字段;下标从1开始显示序号
    for (const cellData of Object.values(row)) {
      const td = document.createElement("td");
      td.textContent = cellData;
      tr.appendChild(td);
    }
    tbody.appendChild(tr);
  });
  // 补足空行
  const rowCount = rowsData.length;
  for (let i = rowCount; i < pageSize; i++) {
    const tr = document.createElement("tr");
    for (let j = 0; j < tbody.parentElement.querySelectorAll("th").length; j++) {
      const td = document.createElement("td");
      td.innerHTML = "&nbsp;";
      tr.appendChild(td);
    }
    tbody.appendChild(tr);
  }
}

//导出漏洞数据
async function ExportVuls(){
    // Check if in secure context
    if (!window.isSecureContext) {
      console.error("Page is not in a secure context. Please load the page over HTTPS.");
      alert("无法导出文件:请使用 HTTPS 访问页面。");
      return;
    }

    // 如果 dataArr 为空,返回提示
    if (!allVuls || allVuls.length === 0) {
      alert("没有数据可导出");
      return;
    }

    // 在 CSV 的开头加上 BOM,用于 Excel 识别 UTF-8 编码
    let csv = "\uFEFF" + "序号,节点路径,漏洞类型,漏洞级别,漏洞说明\n"; // 添加 BOM 防乱码
    allVuls.forEach((item, i) => {
      csv +=
          [
            escapeCsvField(item[0] || ""),
            escapeCsvField(item[1] || ""),
            escapeCsvField(item[2] || ""),
            escapeCsvField(item[3] || ""),
            escapeCsvField(item[4] || ""),
          ].join(",") + "\n";
    });
    const blob = new Blob([csv], { type: "text/csv;charset=utf-8;" });
    const url = URL.createObjectURL(blob);
    const link = document.createElement("a");
    link.href = url;
    link.download = "漏洞数据.csv";
    link.click();
    URL.revokeObjectURL(url);
}

// 绑定漏洞数据查询按钮事件
document.getElementById("vulSearchBtn").addEventListener("click", () => {
  searchVulnerabilities();
});
//导出按钮
document.getElementById("vulExportBtn").addEventListener("click",()=>ExportVuls());
// 绑定漏洞数据分页点击事件
document.getElementById("vulPrev").addEventListener("click", (e) => {
  const page = parseInt(e.target.dataset.page, 10);
  renderVulPage(page);
});
document.getElementById("vulNext").addEventListener("click", (e) => {
  const page = parseInt(e.target.dataset.page, 10);
  renderVulPage(page);
});

//***********关联域名**************
  let latestUrls = [], historyUrls = [];
  let urllatestPage = 1, urlhistoryPage = 1;
  const urlModalEl = document.getElementById('urlDataModal');
  const urlModal = new bootstrap.Modal(urlModalEl);

  // table bodies & pagination
  const latestTbody = document.querySelector('#latestUrlTable tbody');
  const latestPrev = document.getElementById('latestUrlPrev');
  const latestNext = document.getElementById('latestUrlNext');

  const historyTbody = document.querySelector('#historyUrlTable tbody');
  const historyPrev = document.getElementById('historyUrlPrev');
  const historyNext = document.getElementById('historyUrlNext');

  // 打开 Modal 时加载
  urlModalEl.addEventListener('show.bs.modal', async ev => {
    const ip = urlModalEl.dataset.ip;
    if (!ip) return;

    //拉 最新关联
    try{
        const latestJson = await postJSON('/api/assets/geturllatest', {ip});
        latestUrls = latestJson.ip_url_data || [];
    } catch (error) {
        console.error("拉取最新关联的url数据失败!",error)
    }

    urllatestPage = 1;
    urlrenderLatest();

    //拉 历史关联
    try{
      const hisJson = await postJSON('/api/assets/geturlhistory', {ip});
      historyUrls = hisJson.ip_url_data || [];
    } catch (error) {
        console.error("拉取历史的关联URL数据失败!",error)
    }
    urlhistoryPage = 1;
    urlrenderHistory();
  });

  function urlrenderLatest() {
    latestTbody.innerHTML = '';
    const start = (urllatestPage - 1) * pageSize;
    const slice = latestUrls.slice(start, start + pageSize);
    slice.forEach((it, idx) => {
      const tr = document.createElement('tr');
      tr.innerHTML = `
        <td>${start+idx+1}</td>
        <td>${it[0]}</td>
        <td>${it[1]}</td>
        <td>${it[2]}</td>
        <td>${it[3]}</td>
        <td>${it[4]}</td>
        <td>${it[5]}</td>
      `;
      latestTbody.appendChild(tr);
    });
    // 空行
    for (let i=slice.length; i<pageSize; i++) {
      const tr = document.createElement('tr');
      tr.innerHTML = '<td colspan="7">&nbsp;</td>';
      latestTbody.appendChild(tr);
    }
    latestPrev.dataset.page = urllatestPage>1?urllatestPage-1:1;
    latestNext.dataset.page = latestUrls.length>urllatestPage*pageSize?urllatestPage+1:urllatestPage;
  }

  function urlrenderHistory() {
    historyTbody.innerHTML = '';
    const start = (urlhistoryPage - 1) * pageSize;
    const slice = historyUrls.slice(start, start + pageSize);
    slice.forEach((it, idx) => {
      const tr = document.createElement('tr');
      tr.innerHTML = `
        <td>${start+idx+1}</td>
        <td>${it[2]}</td>
        <td>${it[0]}</td>
        <td>${it[1]}</td>
      `;
      historyTbody.appendChild(tr);
    });
    for (let i=slice.length; i<pageSize; i++) {
      const tr = document.createElement('tr');
      tr.innerHTML = '<td colspan="7">&nbsp;</td>';
      historyTbody.appendChild(tr);
    }
    historyPrev.dataset.page = urlhistoryPage>1?urlhistoryPage-1:1;
    historyNext.dataset.page = historyUrls.length>urlhistoryPage*pageSize?urlhistoryPage+1:urlhistoryPage;
  }

  // 分页绑定
  latestPrev.addEventListener('click',e=>{e.preventDefault(); urllatestPage=+e.target.dataset.page; renderLatest();});
  latestNext.addEventListener('click',e=>{e.preventDefault(); urllatestPage=+e.target.dataset.page; renderLatest();});
  historyPrev.addEventListener('click',e=>{e.preventDefault(); urlhistoryPage=+e.target.dataset.page; renderHistory();});
  historyNext.addEventListener('click',e=>{e.preventDefault(); urlhistoryPage=+e.target.dataset.page; renderHistory();});

  // 打开函数
  window.showRelatedDomain = function(ip) {
    urlModalEl.dataset.ip = ip;
    urlModal.show();
  };

  // 导出(示例 CSV,仅最新/历史当前页)
  document.getElementById('exportUrlData').onclick = () => {
    const active = document.querySelector('#urlTabContent .tab-pane.active').id;
    if (active==='urlLatestPane') {
      let rows=[['序号','域名','子域名','注册人','注册邮箱','创建时间','过期时间']];
      latestUrls.forEach((it,i)=>{
        rows.push([i+1,it[0],it[1],it[2],it[3],it[4],it[5]]);
      });
      urldownloadCSV(rows,'最新关联域名.csv');
    } else {
      let rows=[['序号','时间','变化类型','关联域名']];
      historyUrls.forEach((it,i)=>{
        rows.push([i+1,it[2],it[0],it[1]]);
      });
      urldownloadCSV(rows,'历史关联域名.csv');
    }
  };

  function urldownloadCSV(arr, fname) {
    const bom='\uFEFF';
    const csv = arr.map(r => r.map(v=>`"${v}"`).join(',')).join('\r\n');

    const blob = new Blob([bom+csv], {type:'text/csv;charset=utf-8;'});
    const a = document.createElement('a');
    a.href=URL.createObjectURL(blob);
    a.download=fname; a.click();
    URL.revokeObjectURL(a.href);
  }

//***********删除**************
async function confirmDeleteIp(IP) {
    if (!confirm('确认删除?')) return;
    try{
        const redata = await postJSON('/api/assets/delipassets', {IP});
        bsuccess = redata.bsuccess;
        error = redata.error;
        if(bsuccess){
            alert("删除IP资产成功!");
            loadIpAssets(); //刷新数据 --重新拉取数据后刷新,可以考虑本地数据修改后刷新页面。
        }
        else{
            alert("删除IP资产失败!",error)
        }
    } catch (error) {
        console.error("删除IP资产失败!",error);
    }
}

//---------------------------URL-Assets Tab----------------------------
const urlInfoModalEl = document.getElementById("urlBasicInfoModal")
const urlInfoModal = new bootstrap.Modal(urlInfoModalEl);
let urlAssets = [],currentUrlPage = 1

async function loadUrlAssets(page=1) {
    const url_filter = document.getElementById("urlFilter").value.trim();
    const user_filter = document.getElementById("ownerFilter").value.trim();
    const email_filter = document.getElementById("emailFilter").value;
    try {
        const data = await postJSON("/api/assets/geturlassets",{url_filter,user_filter,email_filter})
        urlAssets = data.url_assets || [];
        renderUrlTable(page);   //刷新表格
      } catch (error) {
        console.error("查询记录出错:", error);
        alert("查询失败!");
      }
}

//刷新Url-assets表格
function renderUrlTable(page) {
  currentUrlPage = page;    //查询数据了,从第一页显示
  const start = (currentUrlPage-1)*pageSize;
  const slice = urlAssets.slice(start, start+pageSize);
  const tbody = document.querySelector('#urlTable tbody');
  tbody.innerHTML = '';
  for (let i=0; i<slice.length; i++) {
    const item = slice[i];
    const tr = document.createElement('tr');
    tr.innerHTML = `
      <td>${start+i+1}</td>
      <td>${item[1]}</td>
      <td>${item[2]}</td>
      <td>${item[3]}</td>
      <td>${item[4]}</td>
      <td>${item[5]}</td>
      <td>${item[6]}</td>
      <td>
        <button class="btn btn-sm btn-info asset-op-btn" onclick="urlBasicInfo('${start+i}')">基本信息</button>
        <button class="btn btn-sm btn-info asset-op-btn" onclick="urlIPData('${item[0]}')">指向IP</button>
        <button class="btn btn-sm btn-danger asset-op-btn" onclick="confirmDeleteUrl('${item[0]}','${start+i}')">删除</button>
      </td>
    `;
    tbody.appendChild(tr);
  }
  // 补足空行
  for (let i=slice.length; i<pageSize; i++) {
    const tr = document.createElement('tr');
    tr.innerHTML = '<td colspan="9">&nbsp;</td>';
    tbody.appendChild(tr);
  }
  // 更新分页
  document.getElementById('urlPrev').dataset.page = currentUrlPage>1?currentUrlPage-1:1;
  document.getElementById('urlNext').dataset.page = urlAssets.length>currentUrlPage*pageSize?currentUrlPage+1:currentUrlPage;
}

//导出域名信息
async function exportUrlAssets(){
      // 构造 CSV 文本
      let rows = [
        ['序号', '域名', '所属用户', '注册邮箱', '最新检测时间','过期日期','IP'].map(fmtCell).join(',')
      ];
      urlAssets.forEach((row, i) => {
        rows.push([
          (i + 1).toString(),
          row[1].toString(),
          row[2] || '',
          row[3] || '',
          row[4] || '',
          row[5] || '',
          row[6] || '',
        ].map(fmtCell).join(','));
      });

    const csv = '\uFEFF' + rows.join('\r\n');  // 加 BOM

    downloadCSV(csv, `url_assets.csv`);
}

// 分页按钮
document.getElementById('urlPrev').addEventListener('click', e => { e.preventDefault(); renderUrlTable(+e.target.dataset.page); });
document.getElementById('urlNext').addEventListener('click', e => { e.preventDefault(); renderUrlTable(+e.target.dataset.page); });
// 查询
document.getElementById('urlSearchBtn').addEventListener('click', () => loadUrlAssets(1));
//导出
document.getElementById("urlExportBtn").addEventListener('click',() => exportUrlAssets())

//***********URL基本信息modal**************
const urlEl = document.getElementById("info_url");
const timeEl = document.getElementById("url_scanTime");
const registrantEl = document.getElementById("url_register");
const emailEl = document.getElementById("url_email");
const ownerEl = document.getElementById("url_owner");
const contactEl = document.getElementById("urlcontactName");
const tellEl = document.getElementById("urlcontactPhone");
const btnChose = document.getElementById("urlbtnChooseOwner");
const btnDelO = document.getElementById("urlbtnDelOwner");
const btnSaveurl = document.getElementById("saveUrlInfo");
let cur_url_id = 0;
//url.ID,url.URL,au.uname,url.emails,url.update_time,url.expiration_date,ip.ip_count,url.Registrant,au.tellnum,au.tell_username
async function urlBasicInfo(index){
    const url_info = urlAssets[index];
    cur_url_id = url_info[0];
    urlEl.textContent = url_info[1];
    timeEl.textContent = url_info[4];
    registrantEl.textContent = url_info[7];
    emailEl.textContent = url_info[3];
    ownerEl.textContent = url_info[2];
    contactEl.textContent = url_info[9];
    tellEl.textContent = url_info[8];
    selectedOwnerId = url_info[10];
    urlInfoModal.show();
}

btnChose.addEventListener("click",()=>{ //和IP共用一个模态框
    curModal = "url"
    ownerDrawer.show();
    loadOwners(ownerSearchEl.value.trim());
});

btnDelO.addEventListener("click",()=>{
    selectedOwnerId = null;
    ownerEl.textContent = '';
    contactEl.textContent = '';
    tellEl.textContent = '';
});

btnSaveurl.addEventListener("click",async ()=>saveUrlInfo());

async function saveUrlInfo(){
  const payload = {
    cur_url_id: cur_url_id,
    owner_id: selectedOwnerId
  };
  try {
    const ret = await postJSON('/api/assets/updateurlinfo', payload);
    bsuccess = ret.bsuccess;
    if (bsuccess) {
      showToast('修改成功', 'success');
      loadUrlAssets(currentIpPage); //刷新整个数据。。--要改成局部刷新或本地刷新
    } else {
      showToast(ret.message || '修改失败', 'danger');
    }
  } catch (e) {
    showToast(`提交失败:${e.message}`, 'danger');
  }
}

//***********指向IPmodal**************
const urlIPModalEl = document.getElementById("toIpModal");
const urlIPModal = new bootstrap.Modal(urlIPModalEl);
let last_to_ips = [],his_to_ips=[];
let last_to_ips_page = 1, his_to_ip_page = 1;
const latestUTPTableBody   = document.querySelector('#latesttoipTable tbody');
const latestUTPPrevBtn     = document.getElementById('latesttoipPrev');
const latestUTPNextBtn     = document.getElementById('latesttoipNext');
const historyutpTableBody  = document.querySelector('#historytoipTable tbody');
const hisPortUTPPrevBtn     = document.getElementById('historytoipPrev');
const hisPortUTPNextBtn     = document.getElementById('historytoipNext');

const exportUTPDataBtn = document.getElementById("exporttoipData");
exportUTPDataBtn.addEventListener('click',()=>exportUTPdata());

async function urlIPData(cur_url_id){
    //获取数据
    const payload ={cur_url_id};
    try {
        const ret = await postJSON('/api/assets/geturltoIP', payload);
        last_to_ips = ret.last_to_ips;
        his_to_ips = ret.his_to_ips;
        renderUTPlast();
        renderUTPhis();
        urlIPModal.show();
      } catch (e) {
        showToast(`获取指向IP数据失败:${e.message}`, 'danger');
      }
}

function renderUTPlast(){
    latestUTPTableBody.innerHTML = '';
    const start = (last_to_ips_page - 1) * pageSize;
    const slice = last_to_ips.slice(start, start + pageSize);
    slice.forEach((it, idx) => {
      const tr = document.createElement('tr');
      tr.innerHTML = `
        <td>${start+idx+1}</td>
        <td>${it[0]}</td>
        <td>${it[1]}</td>
      `;
      latestUTPTableBody.appendChild(tr);
    });
    // 空行
    for (let i=slice.length; i<pageSize; i++) {
      const tr = document.createElement('tr');
      tr.innerHTML = '<td colspan="7">&nbsp;</td>';
      latestUTPTableBody.appendChild(tr);
    }
    latestUTPPrevBtn.dataset.page = last_to_ips_page>1?last_to_ips_page-1:1;
    latestUTPNextBtn.dataset.page = last_to_ips.length>last_to_ips_page*pageSize?last_to_ips_page+1:last_to_ips_page;
}

function renderUTPhis(){
    historyutpTableBody.innerHTML = '';
    const start = (his_to_ip_page - 1) * pageSize;
    const slice = his_to_ips.slice(start, start + pageSize);
    slice.forEach((it, idx) => {
      const tr = document.createElement('tr');
      tr.innerHTML = `
        <td>${start+idx+1}</td>
        <td>${it[0]}</td>
        <td>${it[1]}</td>
      `;
      historyutpTableBody.appendChild(tr);
    });
    // 空行
    for (let i=slice.length; i<pageSize; i++) {
      const tr = document.createElement('tr');
      tr.innerHTML = '<td colspan="7">&nbsp;</td>';
      historyutpTableBody.appendChild(tr);
    }
    hisPortUTPPrevBtn.dataset.page = his_to_ip_page>1?his_to_ip_page-1:1;
    hisPortUTPNextBtn.dataset.page = his_to_ips.length>his_to_ip_page*pageSize?his_to_ip_page+1:his_to_ip_page;
}

// 分页绑定
latestUTPPrevBtn.addEventListener('click',e=>{e.preventDefault(); last_to_ips_page=+e.target.dataset.page; renderLatest();});
latestUTPNextBtn.addEventListener('click',e=>{e.preventDefault(); last_to_ips_page=+e.target.dataset.page; renderLatest();});
hisPortUTPPrevBtn.addEventListener('click',e=>{e.preventDefault(); his_to_ip_page=+e.target.dataset.page; renderHistory();});
hisPortUTPNextBtn.addEventListener('click',e=>{e.preventDefault(); his_to_ip_page=+e.target.dataset.page; renderHistory();});

//导出指向IP的数据
async function exportUTPdata(){
    // 构造 CSV 文本
    const active = document.querySelector('#toipTabContent .tab-pane.active').id;
    if (active==='toipLatestPane') {
      let rows=[['序号','IP地址','关联时间']];
      last_to_ips.forEach((it,i)=>{
        rows.push([i+1,it[0],it[1]]);
      });
      urldownloadCSV(rows,'当前指向IP的数据.csv');
    } else {
      let rows=[['序号','IP地址','取关时间']];
      his_to_ips.forEach((it,i)=>{
        rows.push([i+1,it[0],it[1]]);
      });
      urldownloadCSV(rows,'历史指向IP的数据.csv');
    }
}


//***********删除URL**************
async function confirmDeleteUrl(url,index){
    if (!confirm('确认删除?')) return;
    try{
        const redata = await postJSON('/api/assets/delurlassets', {url});
        bsuccess = redata.bsuccess;
        error = redata.error;
        if(bsuccess){
            alert("删除域名资产成功!");
            //loadUrlAssets(); //刷新数据

            //只更新本地数据
            urlAssets.splice(index,1);
            renderUrlTable(currentUrlPage);//刷新页面 --不重新请求数据了
        }
        else{
            alert("删除域名资产失败!",error);
        }
    } catch (error) {
        console.error("删除域名资产失败!",error);
    }
}