interface DokployProject {
  projectId: string;
  name: string;
  description?: string;
  createdAt: string;
}

interface DokployApplication {
  applicationId: string;
  name: string;
  appName: string;
  projectId: string;
  applicationStatus: 'idle' | 'running' | 'done' | 'error';
  createdAt: string;
}

interface DokployDomain {
  domainId: string;
  host: string;
  port: number;
  https: boolean;
  applicationId: string;
}

interface CreateProjectRequest {
  name: string;
  description?: string;
}

interface CreateApplicationRequest {
  name: string;
  appName: string;
  projectId: string;
  dockerImage?: string;
  sourceType?: 'docker' | 'git' | 'github';
}

interface CreateDomainRequest {
  host: string;
  applicationId: string;
  https?: boolean;
  port?: number;
}

interface DokployAPIError {
  message: string;
  statusCode?: number;
}

export class DokployClient {
  private baseUrl: string;
  private apiToken: string;

  constructor(baseUrl: string, apiToken: string) {
    if (!baseUrl) {
      throw new Error('DOKPLOY_URL is required');
    }
    if (!apiToken) {
      throw new Error('DOKPLOY_API_TOKEN is required');
    }
    this.baseUrl = baseUrl.replace(/\/$/, '');
    this.apiToken = apiToken;
  }

  private async request<T>(
    method: string,
    endpoint: string,
    body?: unknown
  ): Promise<T> {
    const url = `${this.baseUrl}/api${endpoint}`;

    const response = await fetch(url, {
      method,
      headers: {
        'x-api-key': this.apiToken,
        'Content-Type': 'application/json',
      },
      body: body ? JSON.stringify(body) : undefined,
    });

    const text = await response.text();
    
    if (!response.ok) {
      let errorMessage = `Dokploy API error (${response.status})`;
      try {
        const errorData = JSON.parse(text) as DokployAPIError;
        errorMessage = errorData.message || errorMessage;
      } catch {
        errorMessage = text || errorMessage;
      }
      throw new Error(errorMessage);
    }

    if (!text) {
      return {} as T;
    }

    return JSON.parse(text) as T;
  }

  async createProject(name: string, description?: string): Promise<DokployProject> {
    return this.request<DokployProject>('POST', '/project.create', {
      name,
      description,
    } satisfies CreateProjectRequest);
  }

  async getProjects(): Promise<DokployProject[]> {
    return this.request<DokployProject[]>('GET', '/project.all');
  }

  async findProjectByName(name: string): Promise<DokployProject | null> {
    const projects = await this.getProjects();
    return projects.find(p => p.name === name) || null;
  }

  async createApplication(
    name: string,
    projectId: string,
    dockerImage?: string
  ): Promise<DokployApplication> {
    return this.request<DokployApplication>('POST', '/application.create', {
      name,
      appName: name.toLowerCase().replace(/[^a-z0-9-]/g, '-'),
      projectId,
      dockerImage,
      sourceType: dockerImage ? 'docker' : undefined,
    } satisfies CreateApplicationRequest);
  }

  async getApplication(applicationId: string): Promise<DokployApplication> {
    return this.request<DokployApplication>(
      'GET',
      `/application.one?applicationId=${applicationId}`
    );
  }

  async createDomain(
    host: string,
    applicationId: string,
    https = true,
    port = 8080
  ): Promise<DokployDomain> {
    return this.request<DokployDomain>('POST', '/domain.create', {
      host,
      applicationId,
      https,
      port,
    } satisfies CreateDomainRequest);
  }

  async setApplicationEnv(applicationId: string, env: string): Promise<void> {
    await this.request('POST', '/application.update', { 
      applicationId, 
      env 
    });
  }

  async addApplicationLabel(applicationId: string, key: string, value: string): Promise<void> {
    await this.request('POST', '/application.update', { 
      applicationId,
      dockerLabels: `${key}=${value}`
    });
  }

  async deployApplication(applicationId: string): Promise<void> {
    await this.request('POST', '/application.deploy', { applicationId });
  }

  async deleteProject(projectId: string): Promise<void> {
    await this.request('POST', '/project.remove', { projectId });
  }

  async deleteApplication(applicationId: string): Promise<void> {
    await this.request('POST', '/application.delete', { applicationId });
  }

  async testConnection(): Promise<{ success: boolean; message: string; projectCount?: number }> {
    try {
      const projects = await this.getProjects();
      return {
        success: true,
        message: `Connected to Dokploy API. Found ${projects.length} projects.`,
        projectCount: projects.length
      };
    } catch (error) {
      return {
        success: false,
        message: error instanceof Error ? error.message : 'Unknown error'
      };
    }
  }
}

export function createDokployClient(): DokployClient {
  const baseUrl = process.env.DOKPLOY_URL;
  const apiToken = process.env.DOKPLOY_API_TOKEN;

  if (!baseUrl) {
    throw new Error('DOKPLOY_URL environment variable is not set');
  }
  if (!apiToken) {
    throw new Error('DOKPLOY_API_TOKEN environment variable is not set');
  }

  return new DokployClient(baseUrl, apiToken);
}
