import { toFrappeFilterArray } from '../utils/listFilterUtils';

export interface Project {
  name: string;
  project_name?: string;
  status?: string;
  priority?: string;
  customer?: string;
  customer_name?: string;
  company?: string;
  percent_complete?: number;
  expected_start_date?: string;
  expected_end_date?: string;
  estimated_costing?: number;
  total_sales_amount?: number;
  total_costing_amount?: number;
  total_billable_amount?: number;
  total_billed_amount?: number;
  notes?: string;
  creation?: string;
  modified?: string;
  [key: string]: any;
}

export interface TaskRow {
  name: string;
  subject?: string;
  project?: string;
  status?: string;
  priority?: string;
  exp_start_date?: string;
  exp_end_date?: string;
  progress?: number;
  _assign?: string;
  [key: string]: any;
}

export interface ProjectListParams {
  filters?: Record<string, any>;
  limit_start?: number;
  limit_page_length?: number;
  order_by?: string;
}

class ProjectService {
  private baseURL = '';

  private async getCSRFToken(): Promise<string | null> {
    if (typeof window === 'undefined') return null;
    if ((window as any).csrf_token) return (window as any).csrf_token;
    try {
      const res = await fetch('/api/method/frappe.sessions.get_csrf_token', { credentials: 'include' });
      if (res.ok) {
        const json = await res.json();
        if (json.message) {
          (window as any).csrf_token = json.message;
          return json.message;
        }
      }
    } catch {
      /* ignore */
    }
    return null;
  }

  private async getHeaders(): Promise<Record<string, string>> {
    const h: Record<string, string> = { 'Content-Type': 'application/json', Accept: 'application/json' };
    const csrf = await this.getCSRFToken();
    if (csrf) h['X-Frappe-CSRF-Token'] = csrf;
    return h;
  }

  private async fetchJson(url: string, opts: RequestInit = {}): Promise<any> {
    const headers = await this.getHeaders();
    const r = await fetch(url, { credentials: 'include', headers: { ...headers, ...(opts.headers || {}) }, ...opts });
    const body = await r.json().catch(() => ({}));
    if (!r.ok) throw new Error(body.message || body.exc || `HTTP ${r.status}`);
    return body;
  }

  async getProjects(params: ProjectListParams = {}): Promise<{ data: Project[] }> {
    const { filters = {}, limit_start = 0, limit_page_length = 20, order_by = 'modified desc' } = params;
    const q = new URLSearchParams();
    q.set(
      'fields',
      JSON.stringify([
        'name',
        'project_name',
        'status',
        'priority',
        'customer',
        'customer_name',
        'company',
        'percent_complete',
        'expected_start_date',
        'expected_end_date',
        'creation',
        'modified',
      ])
    );
    q.set('limit_start', String(limit_start));
    q.set('limit_page_length', String(limit_page_length));
    q.set('order_by', order_by);
    if (Object.keys(filters).length > 0) {
      const fa = toFrappeFilterArray(filters);
      if (fa.length > 0) q.set('filters', JSON.stringify(fa));
    }
    const r = await this.fetchJson(`${this.baseURL}/api/resource/Project?${q}`);
    return { data: r.data || [] };
  }

  async getProjectCount(filters: Record<string, any> = {}): Promise<number> {
    const q = new URLSearchParams();
    q.set('fields', JSON.stringify(['count(name) as count']));
    if (Object.keys(filters).length > 0) {
      const fa = toFrappeFilterArray(filters);
      if (fa.length > 0) q.set('filters', JSON.stringify(fa));
    }
    try {
      const r = await this.fetchJson(`${this.baseURL}/api/resource/Project?${q}`);
      return r.data?.[0]?.count ?? 0;
    } catch {
      return 0;
    }
  }

  async getProject(name: string): Promise<Project> {
    const r = await this.fetchJson(`${this.baseURL}/api/resource/Project/${encodeURIComponent(name)}`);
    return r.data;
  }

  async getTasksForProject(projectName: string, opts: { limit?: number; order_by?: string } = {}): Promise<TaskRow[]> {
    const { limit = 200, order_by = 'modified desc' } = opts;
    const filters = [['Task', 'project', '=', projectName]];
    const q = new URLSearchParams();
    q.set(
      'fields',
      JSON.stringify([
        'name',
        'subject',
        'project',
        'status',
        'priority',
        'exp_start_date',
        'exp_end_date',
        'progress',
        '_assign',
        'modified',
      ])
    );
    q.set('filters', JSON.stringify(filters));
    q.set('limit_page_length', String(limit));
    q.set('order_by', order_by);
    const r = await this.fetchJson(`${this.baseURL}/api/resource/Task?${q}`);
    return r.data || [];
  }

  async getIssuesForProject(projectName: string, limit = 100): Promise<any[]> {
    const filters = [['Issue', 'project', '=', projectName]];
    const q = new URLSearchParams();
    q.set('fields', JSON.stringify(['name', 'subject', 'status', 'priority', 'creation', 'project']));
    q.set('filters', JSON.stringify(filters));
    q.set('limit_page_length', String(limit));
    q.set('order_by', 'modified desc');
    try {
      const r = await this.fetchJson(`${this.baseURL}/api/resource/Issue?${q}`);
      return r.data || [];
    } catch {
      return [];
    }
  }

  async copyProject(name: string): Promise<string> {
    const headers = await this.getHeaders();
    const r = await fetch(`${this.baseURL}/api/method/frappe.client.copy_doc`, {
      method: 'POST',
      credentials: 'include',
      headers,
      body: JSON.stringify({ doctype: 'Project', name }),
    });
    const body = await r.json().catch(() => ({}));
    if (!r.ok) throw new Error(body.message || body.exc || 'Duplicate failed');
    const msg = body.message;
    if (typeof msg === 'string') return msg;
    if (msg && typeof msg === 'object' && msg.name) return msg.name;
    throw new Error('Duplicate did not return new project name');
  }

  async createProject(doc: Record<string, any>): Promise<Project> {
    const headers = await this.getHeaders();
    const r = await fetch(`${this.baseURL}/api/resource/Project`, {
      method: 'POST',
      credentials: 'include',
      headers,
      body: JSON.stringify(doc),
    });
    const body = await r.json().catch(() => ({}));
    if (!r.ok) throw new Error(body.message || body.exc || body._server_messages || `HTTP ${r.status}`);
    return body.data;
  }

  async updateProject(name: string, doc: Record<string, any>): Promise<Project> {
    const headers = await this.getHeaders();
    const r = await fetch(`${this.baseURL}/api/resource/Project/${encodeURIComponent(name)}`, {
      method: 'PUT',
      credentials: 'include',
      headers,
      body: JSON.stringify(doc),
    });
    const body = await r.json().catch(() => ({}));
    if (!r.ok) throw new Error(body.message || body.exc || body._server_messages || `HTTP ${r.status}`);
    return body.data;
  }
}

const projectService = new ProjectService();
export default projectService;
