import { fileURLToPath as __fileURLToPath } from 'url';
import { dirname as __pathDirname } from 'path';
const __filename = __fileURLToPath(import.meta.url);
const __dirname = __pathDirname(__filename);
import { logger } from "../../core/monitoring/logger.js";
class LinearClient {
  config;
  baseUrl;
  rateLimitState = {
    remaining: 1500,
    // Linear's default limit
    resetAt: Date.now() + 36e5,
    retryAfter: 0
  };
  requestQueue = [];
  isProcessingQueue = false;
  minRequestInterval = 100;
  // Minimum ms between requests
  lastRequestTime = 0;
  constructor(config) {
    this.config = config;
    this.baseUrl = config.baseUrl || "https://api.linear.app";
    if (!config.apiKey) {
      throw new Error("Linear API key is required");
    }
  }
  /**
   * Wait for rate limit to reset if needed
   */
  async waitForRateLimit() {
    const now = Date.now();
    if (this.rateLimitState.retryAfter > now) {
      const waitTime = this.rateLimitState.retryAfter - now;
      logger.warn(`Rate limited, waiting ${Math.ceil(waitTime / 1e3)}s`);
      await this.sleep(waitTime);
    }
    if (this.rateLimitState.remaining <= 5) {
      if (this.rateLimitState.resetAt > now) {
        const waitTime = this.rateLimitState.resetAt - now;
        logger.warn(
          `Rate limit nearly exhausted, waiting ${Math.ceil(waitTime / 1e3)}s for reset`
        );
        await this.sleep(Math.min(waitTime, 6e4));
      }
    }
    const timeSinceLastRequest = now - this.lastRequestTime;
    if (timeSinceLastRequest < this.minRequestInterval) {
      await this.sleep(this.minRequestInterval - timeSinceLastRequest);
    }
  }
  sleep(ms) {
    return new Promise((resolve) => setTimeout(resolve, ms));
  }
  /**
   * Update rate limit state from response headers
   */
  updateRateLimitState(response) {
    const remaining = response.headers.get("x-ratelimit-remaining");
    const reset = response.headers.get("x-ratelimit-reset");
    const retryAfter = response.headers.get("retry-after");
    if (remaining !== null) {
      this.rateLimitState.remaining = parseInt(remaining, 10);
    }
    if (reset !== null) {
      this.rateLimitState.resetAt = parseInt(reset, 10) * 1e3;
    }
    if (retryAfter !== null) {
      this.rateLimitState.retryAfter = Date.now() + parseInt(retryAfter, 10) * 1e3;
    }
  }
  /**
   * Execute GraphQL query against Linear API with rate limiting
   */
  async graphql(query, variables, retries = 3, allowAuthRefresh = true) {
    await this.waitForRateLimit();
    this.lastRequestTime = Date.now();
    const authHeader = this.config.useBearer ? `Bearer ${this.config.apiKey}` : this.config.apiKey;
    let response = await fetch(`${this.baseUrl}/graphql`, {
      method: "POST",
      headers: {
        Authorization: authHeader,
        "Content-Type": "application/json"
      },
      body: JSON.stringify({
        query,
        variables
      })
    });
    this.updateRateLimitState(response);
    if (response.status === 401 && this.config.onUnauthorized && allowAuthRefresh) {
      try {
        const newToken = await this.config.onUnauthorized();
        this.config.apiKey = newToken;
        const retryHeader = this.config.useBearer ? `Bearer ${newToken}` : newToken;
        response = await fetch(`${this.baseUrl}/graphql`, {
          method: "POST",
          headers: {
            Authorization: retryHeader,
            "Content-Type": "application/json"
          },
          body: JSON.stringify({ query, variables })
        });
        this.updateRateLimitState(response);
      } catch (e) {
      }
    }
    if (response.status === 429) {
      if (retries > 0) {
        const retryAfter = response.headers.get("retry-after");
        const waitTime = retryAfter ? parseInt(retryAfter, 10) * 1e3 : 6e4;
        logger.warn(
          `Rate limited (429), retrying in ${waitTime / 1e3}s (${retries} retries left)`
        );
        this.rateLimitState.retryAfter = Date.now() + waitTime;
        await this.sleep(waitTime);
        return this.graphql(query, variables, retries - 1, allowAuthRefresh);
      }
      throw new Error("Linear API rate limit exceeded after retries");
    }
    if (!response.ok) {
      const errorText = await response.text();
      logger.error(
        "Linear API error response:",
        new Error(`${response.status}: ${errorText}`)
      );
      throw new Error(
        `Linear API error: ${response.status} ${response.statusText} - ${errorText}`
      );
    }
    const result = await response.json();
    if (result.errors) {
      const rateLimitError = result.errors.find(
        (e) => e.message.toLowerCase().includes("rate limit") || e.message.toLowerCase().includes("usage limit")
      );
      if (rateLimitError && retries > 0) {
        const waitTime = 6e4;
        logger.warn(
          `GraphQL rate limit error, retrying in ${waitTime / 1e3}s (${retries} retries left)`
        );
        this.rateLimitState.retryAfter = Date.now() + waitTime;
        await this.sleep(waitTime);
        return this.graphql(query, variables, retries - 1);
      }
      logger.error("Linear GraphQL errors:", { errors: result.errors });
      throw new Error(`Linear GraphQL error: ${result.errors[0].message}`);
    }
    return result.data;
  }
  /**
   * Create a new issue in Linear
   */
  async createIssue(input) {
    const mutation = `
      mutation CreateIssue($input: IssueCreateInput!) {
        issueCreate(input: $input) {
          success
          issue {
            id
            identifier
            title
            description
            state {
              id
              name
              type
            }
            priority
            assignee {
              id
              name
              email
            }
            estimate
            labels {
              nodes {
                id
                name
              }
            }
            createdAt
            updatedAt
            url
          }
        }
      }
    `;
    const result = await this.graphql(mutation, { input });
    if (!result.issueCreate.success) {
      throw new Error("Failed to create Linear issue");
    }
    return result.issueCreate.issue;
  }
  /**
   * Update an existing Linear issue
   */
  async updateIssue(issueId, updates) {
    const mutation = `
      mutation UpdateIssue($id: String!, $input: IssueUpdateInput!) {
        issueUpdate(id: $id, input: $input) {
          success
          issue {
            id
            identifier
            title
            description
            state {
              id
              name
              type
            }
            priority
            assignee {
              id
              name
              email
            }
            estimate
            labels {
              nodes {
                id
                name
              }
            }
            createdAt
            updatedAt
            url
          }
        }
      }
    `;
    const result = await this.graphql(mutation, { id: issueId, input: updates });
    if (!result.issueUpdate.success) {
      throw new Error(`Failed to update Linear issue ${issueId}`);
    }
    return result.issueUpdate.issue;
  }
  /**
   * Get issue by ID
   */
  async getIssue(issueId) {
    const query = `
      query GetIssue($id: String!) {
        issue(id: $id) {
          id
          identifier
          title
          description
          state {
            id
            name
            type
          }
          priority
          assignee {
            id
            name
            email
          }
          estimate
          labels {
            nodes {
              id
              name
            }
          }
          createdAt
          updatedAt
          url
        }
      }
    `;
    const result = await this.graphql(query, { id: issueId });
    return result.issue;
  }
  /**
   * Search for issues by identifier (e.g., "SM-123")
   */
  async findIssueByIdentifier(identifier) {
    const query = `
      query FindIssue($filter: IssueFilter!) {
        issues(filter: $filter, first: 1) {
          nodes {
            id
            identifier
            title
            description
            state {
              id
              name
              type
            }
            priority
            assignee {
              id
              name
              email
            }
            estimate
            labels {
              nodes {
                id
                name
              }
            }
            createdAt
            updatedAt
            url
          }
        }
      }
    `;
    const result = await this.graphql(query, {
      filter: {
        number: {
          eq: parseInt(identifier.split("-")[1] || "0") || 0
        }
      }
    });
    return result.issues.nodes[0] || null;
  }
  /**
   * Get team information
   */
  async getTeam(teamId) {
    const query = teamId ? `
        query GetTeam($id: String!) {
          team(id: $id) {
            id
            name
            key
          }
        }
      ` : `
        query GetTeams {
          teams(first: 1) {
            nodes {
              id
              name
              key
            }
          }
        }
      `;
    if (teamId) {
      const result = await this.graphql(query, { id: teamId });
      if (!result.team) {
        throw new Error(`Team ${teamId} not found`);
      }
      return result.team;
    } else {
      const result = await this.graphql(query);
      if (result.teams.nodes.length === 0) {
        throw new Error("No teams found");
      }
      return result.teams.nodes[0];
    }
  }
  /**
   * Get workflow states for a team
   */
  async getWorkflowStates(teamId) {
    const query = `
      query GetWorkflowStates($teamId: String!) {
        team(id: $teamId) {
          states {
            nodes {
              id
              name
              type
              color
            }
          }
        }
      }
    `;
    const result = await this.graphql(query, { teamId });
    return result.team.states.nodes;
  }
  /**
   * Get current viewer/user information
   */
  async getViewer() {
    const query = `
      query GetViewer {
        viewer {
          id
          name
          email
        }
      }
    `;
    const result = await this.graphql(query);
    return result.viewer;
  }
  /**
   * Get all teams for the organization
   */
  async getTeams() {
    const query = `
      query GetTeams {
        teams(first: 50) {
          nodes {
            id
            name
            key
          }
        }
      }
    `;
    const result = await this.graphql(query);
    return result.teams.nodes;
  }
  /**
   * Get issues with filtering options
   */
  async getIssues(options) {
    const query = `
      query GetIssues($filter: IssueFilter, $first: Int!) {
        issues(filter: $filter, first: $first) {
          nodes {
            id
            identifier
            title
            description
            state {
              id
              name
              type
            }
            priority
            assignee {
              id
              name
              email
            }
            estimate
            labels {
              nodes {
                id
                name
              }
            }
            createdAt
            updatedAt
            url
          }
        }
      }
    `;
    const filter = {};
    if (options?.teamId) {
      filter.team = { id: { eq: options.teamId } };
    }
    if (options?.assigneeId) {
      filter.assignee = { id: { eq: options.assigneeId } };
    }
    if (options?.stateType) {
      filter.state = { type: { eq: options.stateType } };
    }
    const result = await this.graphql(query, {
      filter: Object.keys(filter).length > 0 ? filter : void 0,
      first: options?.limit || 50
    });
    return result.issues.nodes;
  }
}
export {
  LinearClient
};
//# sourceMappingURL=client.js.map
