import { Component, OnInit, inject, signal, computed, ElementRef, ViewChild, HostListener } from '@angular/core';
import { CommonModule } from '@angular/common';
import { RouterModule, ActivatedRoute, Router } from '@angular/router';
import { FormBuilder, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms';
import { CdkDrag, CdkDragEnd, CdkDragMove, DragDropModule } from '@angular/cdk/drag-drop';
import { MatCardModule } from '@angular/material/card';
import { MatButtonModule } from '@angular/material/button';
import { MatIconModule } from '@angular/material/icon';
import { MatTooltipModule } from '@angular/material/tooltip';
import { MatFormFieldModule } from '@angular/material/form-field';
import { MatInputModule } from '@angular/material/input';
import { MatSelectModule } from '@angular/material/select';
import { MatCheckboxModule } from '@angular/material/checkbox';
import { MatMenuModule } from '@angular/material/menu';
import { MatDialogModule, MatDialog } from '@angular/material/dialog';
import { MatSnackBarModule } from '@angular/material/snack-bar';
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
import { MatChipsModule } from '@angular/material/chips';
import { MatDividerModule } from '@angular/material/divider';
import { WorkflowService } from '../services/workflow.service';
import { DepartmentService } from '../../departments/services/department.service';
import { NotificationService } from '../../../core/services/notification.service';
import { AuthService } from '../../../core/services/auth.service';
import { WorkflowResponseDto, DepartmentResponseDto } from '../../../api/models';

// Node position interface for canvas positioning
interface NodePosition {
  x: number;
  y: number;
}

// Visual stage interface for canvas display (decoupled from backend model)
interface VisualStage {
  id: string;
  name: string;
  description?: string;
  departmentId: string;
  order: number;
  isRequired: boolean;
  position: NodePosition;
  isSelected: boolean;
  isStartNode?: boolean;
  isEndNode?: boolean;
  connections: string[]; // IDs of connected stages (outgoing)
  metadata?: Record<string, any>;
}

// Connection between stages
interface StageConnection {
  from: string;
  to: string;
  isHighlighted?: boolean;
}

@Component({
  selector: 'app-workflow-builder',
  standalone: true,
  imports: [
    CommonModule,
    RouterModule,
    ReactiveFormsModule,
    DragDropModule,
    MatCardModule,
    MatButtonModule,
    MatIconModule,
    MatTooltipModule,
    MatFormFieldModule,
    MatInputModule,
    MatSelectModule,
    MatCheckboxModule,
    MatMenuModule,
    MatDialogModule,
    MatSnackBarModule,
    MatProgressSpinnerModule,
    MatChipsModule,
    MatDividerModule,
  ],
  templateUrl: './workflow-builder.component.html',
  styleUrls: ['./workflow-builder.component.scss'],
})
export class WorkflowBuilderComponent implements OnInit {
  @ViewChild('canvas', { static: true }) canvasRef!: ElementRef<HTMLDivElement>;
  @ViewChild('svgConnections', { static: true }) svgRef!: ElementRef<SVGElement>;

  private readonly fb = inject(FormBuilder);
  private readonly route = inject(ActivatedRoute);
  private readonly router = inject(Router);
  private readonly dialog = inject(MatDialog);
  private readonly workflowService = inject(WorkflowService);
  private readonly departmentService = inject(DepartmentService);
  private readonly notification = inject(NotificationService);
  private readonly authService = inject(AuthService);

  // State signals
  readonly loading = signal(false);
  readonly saving = signal(false);
  readonly isEditMode = signal(false);
  readonly workflowId = signal<string | null>(null);
  readonly departments = signal<DepartmentResponseDto[]>([]);

  // Canvas state
  readonly stages = signal<VisualStage[]>([]);
  readonly connections = signal<StageConnection[]>([]);
  readonly selectedStageId = signal<string | null>(null);
  readonly isConnecting = signal(false);
  readonly connectingFromId = signal<string | null>(null);
  readonly canvasZoom = signal(1);
  readonly canvasPan = signal<NodePosition>({ x: 0, y: 0 });

  // Tool modes
  readonly currentTool = signal<'select' | 'connect' | 'pan'>('select');

  // Workflow metadata form
  readonly workflowForm = this.fb.nonNullable.group({
    name: ['', Validators.required],
    description: [''],
    workflowType: ['NEW_LICENSE', Validators.required],
    isActive: [true],
  });

  // Stage configuration form
  readonly stageForm = this.fb.nonNullable.group({
    name: ['', Validators.required],
    description: [''],
    departmentId: ['', Validators.required],
    isRequired: [true],
    executionType: ['SEQUENTIAL'],
    completionCriteria: ['ALL'],
    timeoutHours: [72],
  });

  // Computed values
  readonly selectedStage = computed(() => {
    const id = this.selectedStageId();
    return id ? this.stages().find(s => s.id === id) : null;
  });

  readonly hasUnsavedChanges = signal(false);
  readonly stageCount = computed(() => this.stages().length);
  readonly connectionCount = computed(() => this.connections().length);

  // Stage ID counter for new stages
  private stageIdCounter = 0;

  ngOnInit(): void {
    this.loadDepartments();

    const id = this.route.snapshot.paramMap.get('id');
    if (id && id !== 'new') {
      this.workflowId.set(id);
      this.isEditMode.set(true);
      this.loadWorkflow(id);
    } else {
      // Create default start node
      this.addStage('Start', true);
    }
  }

  private loadDepartments(): void {
    this.departmentService.getDepartments(1, 100).subscribe({
      next: (response) => {
        this.departments.set(response?.data ?? []);
      },
    });
  }

  private loadWorkflow(id: string): void {
    this.loading.set(true);
    this.workflowService.getWorkflow(id).subscribe({
      next: (workflow) => {
        this.workflowForm.patchValue({
          name: workflow.name,
          description: workflow.description || '',
          workflowType: workflow.workflowType,
          isActive: workflow.isActive,
        });

        // Get stages from definition (backend format)
        const backendStages = workflow.definition?.stages || [];

        // Convert backend stages to visual stages with positions
        const visualStages: VisualStage[] = backendStages.map((stage, index) => {
          // Find department by code to get departmentId
          const dept = this.departments().find(d =>
            d.code === stage.requiredApprovals?.[0]?.departmentCode
          );

          return {
            id: stage.stageId,
            name: stage.stageName,
            description: stage.metadata?.['description'] || '',
            departmentId: dept?.id || '',
            order: stage.stageOrder,
            isRequired: stage.metadata?.['isRequired'] ?? true,
            position: stage.metadata?.['position'] || this.calculateStagePosition(index, backendStages.length),
            isSelected: false,
            isStartNode: index === 0,
            isEndNode: index === backendStages.length - 1,
            connections: index < backendStages.length - 1 ? [backendStages[index + 1].stageId] : [],
            metadata: stage.metadata,
          };
        });

        this.stages.set(visualStages);
        this.rebuildConnections();
        this.loading.set(false);
      },
      error: () => {
        this.notification.error('Failed to load workflow');
        this.loading.set(false);
      },
    });
  }

  private getResponsiveSpacing(): { startX: number; startY: number; spacingX: number; zigzag: number } {
    const screenWidth = window.innerWidth;
    if (screenWidth <= 480) {
      return { startX: 20, startY: 100, spacingX: 160, zigzag: 20 };
    }
    if (screenWidth <= 768) {
      return { startX: 40, startY: 120, spacingX: 180, zigzag: 25 };
    }
    if (screenWidth <= 1024) {
      return { startX: 60, startY: 150, spacingX: 220, zigzag: 30 };
    }
    return { startX: 100, startY: 200, spacingX: 280, zigzag: 40 };
  }

  private calculateStagePosition(index: number, total: number): NodePosition {
    const { startX, startY, spacingX, zigzag } = this.getResponsiveSpacing();

    // Layout in a horizontal line with some offset for visual clarity
    return {
      x: startX + index * spacingX,
      y: startY + (index % 2) * zigzag, // Slight zigzag for visual interest
    };
  }

  private rebuildConnections(): void {
    const conns: StageConnection[] = [];
    this.stages().forEach(stage => {
      stage.connections.forEach(toId => {
        conns.push({ from: stage.id, to: toId });
      });
    });
    this.connections.set(conns);
  }

  // ========== Stage Management ==========

  addStage(name?: string, isStart?: boolean): void {
    const id = `stage-${++this.stageIdCounter}-${Date.now()}`;
    const existingStages = this.stages();
    const lastStage = existingStages[existingStages.length - 1];
    const { startX, startY, spacingX } = this.getResponsiveSpacing();

    const newStage: VisualStage = {
      id,
      name: name || `Stage ${existingStages.length + 1}`,
      description: '',
      departmentId: '',
      order: existingStages.length + 1,
      isRequired: true,
      position: lastStage
        ? { x: lastStage.position.x + spacingX, y: lastStage.position.y }
        : { x: startX, y: startY },
      isSelected: false,
      isStartNode: isStart || existingStages.length === 0,
      connections: [],
    };

    // Auto-connect from last stage
    if (lastStage && !isStart) {
      const updatedStages = existingStages.map(s =>
        s.id === lastStage.id
          ? { ...s, isEndNode: false, connections: [...s.connections, id] }
          : s
      );
      newStage.isEndNode = true;
      this.stages.set([...updatedStages, newStage]);
    } else {
      this.stages.set([...existingStages, newStage]);
    }

    this.rebuildConnections();
    this.hasUnsavedChanges.set(true);
    this.selectStage(id);
  }

  deleteStage(id: string): void {
    const stageToDelete = this.stages().find(s => s.id === id);
    if (!stageToDelete || stageToDelete.isStartNode) {
      this.notification.error('Cannot delete the start stage');
      return;
    }

    // Remove connections to this stage
    const updatedStages = this.stages()
      .filter(s => s.id !== id)
      .map(s => ({
        ...s,
        connections: s.connections.filter(c => c !== id),
      }));

    // Update order
    updatedStages.forEach((s, i) => {
      s.order = i + 1;
    });

    // Mark last as end node
    if (updatedStages.length > 0) {
      updatedStages[updatedStages.length - 1].isEndNode = true;
    }

    this.stages.set(updatedStages);
    this.rebuildConnections();

    if (this.selectedStageId() === id) {
      this.selectedStageId.set(null);
    }

    this.hasUnsavedChanges.set(true);
  }

  selectStage(id: string | null): void {
    this.selectedStageId.set(id);

    // Update selection state in stages
    this.stages.update(stages =>
      stages.map(s => ({ ...s, isSelected: s.id === id }))
    );

    // Load stage data into form
    if (id) {
      const stage = this.stages().find(s => s.id === id);
      if (stage) {
        this.stageForm.patchValue({
          name: stage.name,
          description: stage.description || '',
          departmentId: stage.departmentId,
          isRequired: stage.isRequired,
          executionType: (stage.metadata as any)?.executionType || 'SEQUENTIAL',
          completionCriteria: (stage.metadata as any)?.completionCriteria || 'ALL',
          timeoutHours: (stage.metadata as any)?.timeoutHours || 72,
        });
      }
    }
  }

  // ========== Drag & Drop ==========

  onStageDragMoved(event: CdkDragMove, stageId: string): void {
    // Update connections in real-time during drag
    this.updateSvgConnections();
  }

  onStageDragEnded(event: CdkDragEnd, stageId: string): void {
    const element = event.source.element.nativeElement;
    const rect = element.getBoundingClientRect();
    const canvasRect = this.canvasRef.nativeElement.getBoundingClientRect();

    const newPosition: NodePosition = {
      x: rect.left - canvasRect.left + this.canvasRef.nativeElement.scrollLeft,
      y: rect.top - canvasRect.top + this.canvasRef.nativeElement.scrollTop,
    };

    this.stages.update(stages =>
      stages.map(s => s.id === stageId ? { ...s, position: newPosition } : s)
    );

    this.hasUnsavedChanges.set(true);
    this.updateSvgConnections();
  }

  // ========== Connection Management ==========

  startConnecting(fromId: string): void {
    if (this.currentTool() !== 'connect') {
      this.currentTool.set('connect');
    }
    this.isConnecting.set(true);
    this.connectingFromId.set(fromId);
  }

  completeConnection(toId: string): void {
    const fromId = this.connectingFromId();
    if (!fromId || fromId === toId) {
      this.cancelConnecting();
      return;
    }

    // Check if connection already exists
    const fromStage = this.stages().find(s => s.id === fromId);
    if (fromStage?.connections.includes(toId)) {
      this.notification.error('Connection already exists');
      this.cancelConnecting();
      return;
    }

    // Add connection
    this.stages.update(stages =>
      stages.map(s =>
        s.id === fromId
          ? { ...s, connections: [...s.connections, toId] }
          : s
      )
    );

    this.rebuildConnections();
    this.hasUnsavedChanges.set(true);
    this.cancelConnecting();
  }

  cancelConnecting(): void {
    this.isConnecting.set(false);
    this.connectingFromId.set(null);
    if (this.currentTool() === 'connect') {
      this.currentTool.set('select');
    }
  }

  deleteConnection(from: string, to: string): void {
    this.stages.update(stages =>
      stages.map(s =>
        s.id === from
          ? { ...s, connections: s.connections.filter(c => c !== to) }
          : s
      )
    );
    this.rebuildConnections();
    this.hasUnsavedChanges.set(true);
  }

  // ========== SVG Connection Rendering ==========

  private getNodeWidth(): number {
    // Responsive node width based on screen size
    const screenWidth = window.innerWidth;
    if (screenWidth <= 480) return 140;
    if (screenWidth <= 768) return 160;
    if (screenWidth <= 1024) return 180;
    if (screenWidth <= 1200) return 200;
    return 240;
  }

  private getNodeHeight(): number {
    const screenWidth = window.innerWidth;
    if (screenWidth <= 768) return 80;
    return 100;
  }

  getConnectionPath(conn: StageConnection): string {
    const fromStage = this.stages().find(s => s.id === conn.from);
    const toStage = this.stages().find(s => s.id === conn.to);

    if (!fromStage || !toStage) return '';

    const nodeWidth = this.getNodeWidth();
    const nodeHeight = this.getNodeHeight();

    const fromX = fromStage.position.x + nodeWidth / 2; // Center of node
    const fromY = fromStage.position.y + nodeHeight;     // Bottom center
    const toX = toStage.position.x + nodeWidth / 2;
    const toY = toStage.position.y - 10;                 // Top center

    // Bezier curve for smooth connection
    const controlOffset = Math.abs(toY - fromY) / 2;

    return `M ${fromX} ${fromY}
            C ${fromX} ${fromY + controlOffset},
              ${toX} ${toY - controlOffset},
              ${toX} ${toY}`;
  }

  updateSvgConnections(): void {
    // Force Angular to re-render SVG connections
    this.connections.update(c => [...c]);
  }

  // ========== Stage Form ==========

  updateSelectedStage(): void {
    const id = this.selectedStageId();
    if (!id) return;

    const formValue = this.stageForm.getRawValue();

    this.stages.update(stages =>
      stages.map(s =>
        s.id === id
          ? {
              ...s,
              name: formValue.name,
              description: formValue.description,
              departmentId: formValue.departmentId,
              isRequired: formValue.isRequired,
              metadata: {
                executionType: formValue.executionType,
                completionCriteria: formValue.completionCriteria,
                timeoutHours: formValue.timeoutHours,
              },
            }
          : s
      )
    );

    this.hasUnsavedChanges.set(true);
  }

  // ========== Workflow Save ==========

  saveWorkflow(): void {
    if (this.workflowForm.invalid) {
      this.notification.error('Please fill in workflow details');
      return;
    }

    if (this.stages().length === 0) {
      this.notification.error('Workflow must have at least one stage');
      return;
    }

    // Validate all stages have departments
    const invalidStages = this.stages().filter(s => !s.departmentId && !s.isStartNode);
    if (invalidStages.length > 0) {
      this.notification.error(`Please assign departments to all stages: ${invalidStages.map(s => s.name).join(', ')}`);
      return;
    }

    this.saving.set(true);

    const workflowData = this.workflowForm.getRawValue();
    const currentUser = this.authService.currentUser();

    // Get departmentId from current user or first stage with a department
    const departmentId = currentUser?.departmentId ||
      this.stages().find(s => s.departmentId)?.departmentId || '';

    // Transform visual stages to backend DTO format
    const dto: any = {
      name: workflowData.name,
      description: workflowData.description || undefined,
      workflowType: workflowData.workflowType,
      stages: this.stages().map((s, index) => {
        const department = this.departments().find(d => d.id === s.departmentId);
        return {
          stageId: s.id,
          stageName: s.name,
          stageOrder: index,  // Backend expects 0-indexed
          executionType: s.metadata?.['executionType'] || 'SEQUENTIAL',
          requiredApprovals: [{
            departmentCode: department?.code || '',
            departmentName: department?.name || 'Unknown',
            canDelegate: false,
          }],
          completionCriteria: s.metadata?.['completionCriteria'] || 'ALL',
          rejectionHandling: 'FAIL_REQUEST',
          metadata: {
            description: s.description,
            position: s.position,
            connections: s.connections,
            timeoutHours: s.metadata?.['timeoutHours'],
            isRequired: s.isRequired,
          },
        };
      }),
      metadata: {
        visualLayout: {
          stages: this.stages().map(s => ({
            id: s.id,
            position: s.position,
          })),
          connections: this.connections(),
        },
      },
    };

    const action$ = this.isEditMode()
      ? this.workflowService.updateWorkflow(this.workflowId()!, dto)
      : this.workflowService.createWorkflow(dto);

    action$.subscribe({
      next: (result) => {
        this.saving.set(false);
        this.hasUnsavedChanges.set(false);
        this.notification.success(this.isEditMode() ? 'Workflow updated' : 'Workflow created');
        this.router.navigate(['/workflows', result.id]);
      },
      error: () => {
        this.saving.set(false);
        this.notification.error('Failed to save workflow');
      },
    });
  }

  // ========== Toolbar Actions ==========

  setTool(tool: 'select' | 'connect' | 'pan'): void {
    this.currentTool.set(tool);
    if (tool !== 'connect') {
      this.cancelConnecting();
    }
  }

  zoomIn(): void {
    this.canvasZoom.update(z => Math.min(z + 0.1, 2));
  }

  zoomOut(): void {
    this.canvasZoom.update(z => Math.max(z - 0.1, 0.5));
  }

  resetZoom(): void {
    this.canvasZoom.set(1);
    this.canvasPan.set({ x: 0, y: 0 });
  }

  autoLayout(): void {
    const stages = this.stages();
    const updatedStages = stages.map((stage, index) => ({
      ...stage,
      position: this.calculateStagePosition(index, stages.length),
    }));
    this.stages.set(updatedStages);
    this.hasUnsavedChanges.set(true);
  }

  // ========== Department Helper ==========

  getDepartmentName(id: string): string {
    return this.departments().find(d => d.id === id)?.name || 'Unassigned';
  }

  getDepartmentIcon(id: string): string {
    const dept = this.departments().find(d => d.id === id);
    if (!dept) return 'business';

    const code = dept.code?.toLowerCase() || '';
    if (code.includes('fire')) return 'local_fire_department';
    if (code.includes('tourism')) return 'flight';
    if (code.includes('municipal')) return 'location_city';
    if (code.includes('health')) return 'health_and_safety';
    return 'business';
  }

  // ========== Window Resize Handler ==========

  @HostListener('window:resize')
  onWindowResize(): void {
    // Update SVG connections when window resizes (node sizes change)
    this.updateSvgConnections();
  }

  // ========== Keyboard Shortcuts ==========

  @HostListener('document:keydown', ['$event'])
  handleKeyboardEvent(event: KeyboardEvent): void {
    // Delete selected stage
    if (event.key === 'Delete' || event.key === 'Backspace') {
      const selected = this.selectedStageId();
      if (selected && !event.target?.toString().includes('Input')) {
        this.deleteStage(selected);
      }
    }

    // Escape to cancel connecting
    if (event.key === 'Escape') {
      this.cancelConnecting();
      this.selectStage(null);
    }

    // Ctrl+S to save
    if (event.ctrlKey && event.key === 's') {
      event.preventDefault();
      this.saveWorkflow();
    }
  }

  // ========== Navigation ==========

  goBack(): void {
    if (this.hasUnsavedChanges()) {
      if (confirm('You have unsaved changes. Are you sure you want to leave?')) {
        this.router.navigate(['/workflows']);
      }
    } else {
      this.router.navigate(['/workflows']);
    }
  }
}
