// Copyright 2020 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import * as Common from '../../core/common/common.js';
import * as SDK from '../../core/sdk/sdk.js';

import {CookieIssue, CookieIssueSubCategory} from './CookieIssue.js';
import type {Issue, IssueCategory} from './Issue.js';
import {IssuesManager} from './IssuesManager.js';

export type IssuesAssociatable = Readonly<SDK.NetworkRequest.NetworkRequest>|SDK.Cookie.Cookie|string;

function issuesAssociatedWithNetworkRequest(issues: Issue[], request: SDK.NetworkRequest.NetworkRequest): Issue[] {
  return issues.filter(issue => {
    for (const affectedRequest of issue.requests()) {
      if (affectedRequest.requestId === request.requestId()) {
        return true;
      }
    }
    return false;
  });
}

function issuesAssociatedWithCookie(issues: Issue[], domain: string, name: string, path: string): Issue[] {
  return issues.filter(issue => {
    for (const cookie of issue.cookies()) {
      if (cookie.domain === domain && cookie.name === name && cookie.path === path) {
        return true;
      }
    }
    return false;
  });
}

/**
 * @throws In case obj has an unsupported type (i.e. not part of the IssuesAssociatble union).
 */
export function issuesAssociatedWith(issues: Issue[], obj: IssuesAssociatable): Issue[] {
  if (obj instanceof SDK.NetworkRequest.NetworkRequest) {
    return issuesAssociatedWithNetworkRequest(issues, obj);
  }
  if (obj instanceof SDK.Cookie.Cookie) {
    return issuesAssociatedWithCookie(issues, obj.domain(), obj.name(), obj.path());
  }
  throw new Error(`issues can not be associated with ${JSON.stringify(obj)}`);
}

export function hasIssues(obj: IssuesAssociatable): boolean {
  const issues = Array.from(IssuesManager.instance().issues());
  return issuesAssociatedWith(issues, obj).length > 0;
}

export function hasIssueOfCategory(obj: IssuesAssociatable, category: IssueCategory): boolean {
  const issues = Array.from(IssuesManager.instance().issues());
  return issuesAssociatedWith(issues, obj).some(issue => issue.getCategory() === category);
}

export function hasThirdPartyPhaseoutCookieIssue(obj: IssuesAssociatable): boolean {
  const issues = Array.from(IssuesManager.instance().issues());
  return issuesAssociatedWith(issues, obj)
      .some(issue => CookieIssue.getSubCategory(issue.code()) === CookieIssueSubCategory.THIRD_PARTY_PHASEOUT_COOKIE);
}

export function hasThirdPartyPhaseoutCookieIssueForDomain(domain: string): boolean {
  const issues = Array.from(IssuesManager.instance().issues());
  const issuesForDomain = issues.filter(issue => Array.from(issue.cookies()).some(cookie => cookie.domain === domain));
  return issuesForDomain.some(
      issue => CookieIssue.getSubCategory(issue.code()) === CookieIssueSubCategory.THIRD_PARTY_PHASEOUT_COOKIE);
}

export async function reveal(obj: IssuesAssociatable, category?: IssueCategory): Promise<void|undefined> {
  if (typeof obj === 'string') {
    const issue = IssuesManager.instance().getIssueById(obj);
    if (issue) {
      return await Common.Revealer.reveal(issue);
    }
  }
  const issues = Array.from(IssuesManager.instance().issues());
  const candidates = issuesAssociatedWith(issues, obj).filter(issue => !category || issue.getCategory() === category);
  if (candidates.length > 0) {
    return await Common.Revealer.reveal(candidates[0]);
  }
}
