import { Router } from 'express'
import { v4 as uuidv4 } from 'uuid'
import { body, param, validationResult } from 'express-validator'
import { db } from '../config/secureDatabase'
import { authenticate, AuthRequest } from '../middleware/auth'
import { 
  OrganizationalUnit, 
  EmployeeUnitAssignment, 
  SpecialPosition,
  DeputyAssignment,
  DeputyDelegation 
} from '@skillmate/shared'
import { logger } from '../utils/logger'

const router = Router()

// Get all organizational units
router.get('/units', authenticate, async (req: AuthRequest, res, next) => {
  try {
    const units = db.prepare(`
      SELECT 
        id, code, name, type, level, parent_id as parentId,
        position_x as positionX, position_y as positionY,
        color, order_index as orderIndex, description,
        has_fuehrungsstelle as hasFuehrungsstelle,
        fuehrungsstelle_name as fuehrungsstelleName,
        is_active as isActive,
        created_at as createdAt,
        updated_at as updatedAt
      FROM organizational_units
      WHERE is_active = 1
      ORDER BY level, order_index, name
    `).all()

    res.json({ success: true, data: units })
  } catch (error) {
    logger.error('Error fetching organizational units:', error)
    next(error)
  }
})

// Get organizational hierarchy (tree structure)
router.get('/hierarchy', authenticate, async (req: AuthRequest, res, next) => {
  try {
    const units = db.prepare(`
      SELECT 
        id, code, name, type, level, parent_id as parentId,
        position_x as positionX, position_y as positionY,
        color, order_index as orderIndex, description,
        has_fuehrungsstelle as hasFuehrungsstelle,
        fuehrungsstelle_name as fuehrungsstelleName,
        is_active as isActive
      FROM organizational_units
      WHERE is_active = 1
      ORDER BY level, order_index, name
    `).all() as any[]

    // Build tree structure
    const unitMap = new Map()
    const rootUnits: any[] = []

    // First pass: create map
    units.forEach(unit => {
      unitMap.set(unit.id, { ...unit, children: [] })
    })

    // Second pass: build tree
    units.forEach(unit => {
      const node = unitMap.get(unit.id)
      if (unit.parentId && unitMap.has(unit.parentId)) {
        unitMap.get(unit.parentId).children.push(node)
      } else {
        rootUnits.push(node)
      }
    })

    res.json({ success: true, data: rootUnits })
  } catch (error) {
    logger.error('Error building organizational hierarchy:', error)
    next(error)
  }
})

// Get single unit with employees
router.get('/units/:id', authenticate, async (req: AuthRequest, res, next) => {
  try {
    const unit = db.prepare(`
      SELECT 
        id, code, name, type, level, parent_id as parentId,
        position_x as positionX, position_y as positionY,
        color, order_index as orderIndex, description,
        has_fuehrungsstelle as hasFuehrungsstelle,
        fuehrungsstelle_name as fuehrungsstelleName,
        is_active as isActive,
        created_at as createdAt,
        updated_at as updatedAt
      FROM organizational_units
      WHERE id = ?
    `).get(req.params.id) as any

    if (!unit) {
      return res.status(404).json({ success: false, error: { message: 'Unit not found' } })
    }

    // Get employees assigned to this unit
    const employees = db.prepare(`
      SELECT 
        e.id, e.first_name as firstName, e.last_name as lastName,
        e.position, e.department, e.email, e.phone, e.photo,
        eua.role, eua.is_primary as isPrimary
      FROM employee_unit_assignments eua
      JOIN employees e ON e.id = eua.employee_id
      WHERE eua.unit_id = ? AND (eua.end_date IS NULL OR eua.end_date > datetime('now'))
      ORDER BY 
        CASE eua.role 
          WHEN 'leiter' THEN 1
          WHEN 'stellvertreter' THEN 2
          WHEN 'beauftragter' THEN 3
          ELSE 4
        END,
        e.last_name, e.first_name
    `).all(req.params.id)

    res.json({ 
      success: true, 
      data: {
        ...unit,
        employees
      }
    })
  } catch (error) {
    logger.error('Error fetching unit details:', error)
    next(error)
  }
})

// Create new organizational unit (Admin only)
router.post('/units', 
  authenticate,
  [
    body('code').notEmpty().trim(),
    body('name').notEmpty().trim(),
    body('type').isIn(['direktion', 'abteilung', 'dezernat', 'sachgebiet', 'teildezernat', 'fuehrungsstelle', 'stabsstelle', 'sondereinheit']),
    body('level').isInt({ min: 0, max: 10 }),
    body('parentId').optional({ checkFalsy: true }).isUUID()
  ],
  async (req: AuthRequest, res: any, next: any) => {
    try {
      const errors = validationResult(req)
      if (!errors.isEmpty()) {
        return res.status(400).json({ success: false, error: { message: 'Invalid input', details: errors.array() } })
      }

      // Check admin permission
      if (req.user?.role !== 'admin') {
        return res.status(403).json({ success: false, error: { message: 'Admin access required' } })
      }

      const { code, name, type, level, parentId, color, description, hasFuehrungsstelle, fuehrungsstelleName } = req.body
      const now = new Date().toISOString()
      const unitId = uuidv4()

      // Check if code already exists
      const existing = db.prepare('SELECT id FROM organizational_units WHERE code = ?').get(code)
      if (existing) {
        return res.status(400).json({ success: false, error: { message: 'Unit code already exists' } })
      }

      // Get max order index for this level
      const maxOrder = db.prepare('SELECT MAX(order_index) as max FROM organizational_units WHERE level = ?').get(level) as any
      const orderIndex = (maxOrder?.max || 0) + 1

      db.prepare(`
        INSERT INTO organizational_units (
          id, code, name, type, level, parent_id,
          color, order_index, description,
          has_fuehrungsstelle, fuehrungsstelle_name,
          is_active, created_at, updated_at
        ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
      `).run(
        unitId, code, name, type, level, parentId || null,
        color || null, orderIndex, description || null,
        hasFuehrungsstelle ? 1 : 0, fuehrungsstelleName || null,
        1, now, now
      )

      res.json({ success: true, data: { id: unitId } })
    } catch (error) {
      logger.error('Error creating organizational unit:', error)
      next(error)
    }
  }
)

// Update organizational unit (Admin only)
router.put('/units/:id',
  authenticate,
  [
    param('id').isUUID(),
    body('name').optional().notEmpty().trim(),
    body('description').optional(),
    body('color').optional(),
    body('parentId').optional({ checkFalsy: true }).isUUID(),
    body('level').optional().isInt({ min: 0, max: 10 }),
    // allow updating persisted canvas positions from admin editor
    body('positionX').optional().isNumeric(),
    body('positionY').optional().isNumeric()
  ],
  async (req: AuthRequest, res: any, next: any) => {
    try {
      if (req.user?.role !== 'admin') {
        return res.status(403).json({ success: false, error: { message: 'Admin access required' } })
      }

      const { name, description, color, hasFuehrungsstelle, fuehrungsstelleName, positionX, positionY, parentId, level } = req.body
      const now = new Date().toISOString()

      // Optional: validate parentId and avoid cycles
      let newParentId: string | null | undefined = undefined
      if (parentId !== undefined) {
        if (parentId === null || parentId === '' ) {
          newParentId = null
        } else {
          const parent = db.prepare('SELECT id, parent_id as parentId FROM organizational_units WHERE id = ?').get(parentId)
          if (!parent) {
            return res.status(404).json({ success: false, error: { message: 'Parent unit not found' } })
          }
          // cycle check: walk up from parent to root; id must not appear
          const targetId = req.params.id
          let cursor: any = parent
          while (cursor && cursor.parentId) {
            if (cursor.parentId === targetId) {
              return res.status(400).json({ success: false, error: { message: 'Cyclic parent assignment is not allowed' } })
            }
            cursor = db.prepare('SELECT id, parent_id as parentId FROM organizational_units WHERE id = ?').get(cursor.parentId)
          }
          newParentId = parentId
        }
      }

      const result = db.prepare(`
        UPDATE organizational_units
        SET name = COALESCE(?, name),
            description = COALESCE(?, description),
            color = COALESCE(?, color),
            has_fuehrungsstelle = COALESCE(?, has_fuehrungsstelle),
            fuehrungsstelle_name = COALESCE(?, fuehrungsstelle_name),
            parent_id = COALESCE(?, parent_id),
            level = COALESCE(?, level),
            position_x = COALESCE(?, position_x),
            position_y = COALESCE(?, position_y),
            updated_at = ?
        WHERE id = ?
      `).run(
        name || null,
        description !== undefined ? description : null,
        color || null,
        hasFuehrungsstelle !== undefined ? (hasFuehrungsstelle ? 1 : 0) : null,
        fuehrungsstelleName || null,
        newParentId !== undefined ? newParentId : null,
        level !== undefined ? Number(level) : null,
        positionX !== undefined ? Math.round(Number(positionX)) : null,
        positionY !== undefined ? Math.round(Number(positionY)) : null,
        now,
        req.params.id
      )

      if (result.changes === 0) {
        return res.status(404).json({ success: false, error: { message: 'Unit not found' } })
      }

      res.json({ success: true })
    } catch (error) {
      logger.error('Error updating organizational unit:', error)
      next(error)
    }
  }
)

// Assign employee to unit
router.post('/assignments',
  authenticate,
  [
    body('employeeId').isUUID(),
    body('unitId').isUUID(),
    body('role').isIn(['leiter', 'stellvertreter', 'mitarbeiter', 'beauftragter'])
  ],
  async (req: AuthRequest, res: any, next: any) => {
    try {
      const errors = validationResult(req)
      if (!errors.isEmpty()) {
        return res.status(400).json({ success: false, error: { message: 'Invalid input', details: errors.array() } })
      }

      const { employeeId, unitId, role, isPrimary } = req.body
      const now = new Date().toISOString()
      const assignmentId = uuidv4()

      // Permission model: users may only assign themselves and only as 'mitarbeiter' or 'beauftragter'
      const isAdmin = req.user?.role === 'admin' || req.user?.role === 'superuser'
      if (!isAdmin) {
        if (!req.user?.employeeId || req.user.employeeId !== employeeId) {
          return res.status(403).json({ success: false, error: { message: 'Cannot assign other employees' } })
        }
        if (!['mitarbeiter', 'beauftragter'].includes(role)) {
          return res.status(403).json({ success: false, error: { message: 'Insufficient role to set this assignment' } })
        }
      }

      // Validate unit exists and is active
      const unit = db.prepare('SELECT id FROM organizational_units WHERE id = ? AND is_active = 1').get(unitId)
      if (!unit) {
        return res.status(404).json({ success: false, error: { message: 'Unit not found' } })
      }

      // Check if assignment already exists
      const existing = db.prepare(`
        SELECT id FROM employee_unit_assignments 
        WHERE employee_id = ? AND unit_id = ? AND role = ? AND end_date IS NULL
      `).get(employeeId, unitId, role)

      if (existing) {
        return res.status(400).json({ success: false, error: { message: 'Assignment already exists' } })
      }

      // If setting as primary, demote all other active assignments for this employee
      if (isPrimary) {
        db.prepare(`
          UPDATE employee_unit_assignments
          SET is_primary = 0, updated_at = ?
          WHERE employee_id = ? AND end_date IS NULL
        `).run(now, employeeId)
      }

      db.prepare(`
        INSERT INTO employee_unit_assignments (
          id, employee_id, unit_id, role,
          start_date, is_primary, created_at, updated_at
        ) VALUES (?, ?, ?, ?, ?, ?, ?, ?)
      `).run(
        assignmentId, employeeId, unitId, role,
        now, isPrimary ? 1 : 0, now, now
      )

      res.json({ success: true, data: { id: assignmentId } })
    } catch (error) {
      logger.error('Error assigning employee to unit:', error)
      next(error)
    }
  }
)

// Get my organizational units
router.get('/my-units', authenticate, async (req: AuthRequest, res, next) => {
  try {
    if (!req.user?.employeeId) {
      return res.json({ success: true, data: [] })
    }

    const units = db.prepare(`
      SELECT 
        ou.id, ou.code, ou.name, ou.type, ou.level,
        eua.role, eua.is_primary as isPrimary
      FROM employee_unit_assignments eua
      JOIN organizational_units ou ON ou.id = eua.unit_id
      WHERE eua.employee_id = ? 
        AND (eua.end_date IS NULL OR eua.end_date > datetime('now'))
        AND ou.is_active = 1
      ORDER BY eua.is_primary DESC, ou.level, ou.name
    `).all(req.user.employeeId)

    res.json({ success: true, data: units })
  } catch (error) {
    logger.error('Error fetching user units:', error)
    next(error)
  }
})

// Get my deputy assignments
router.get('/deputies/my', authenticate, async (req: AuthRequest, res, next) => {
  try {
    if (!req.user?.employeeId) {
      return res.json({ success: true, data: { asDeputy: [], asPrincipal: [] } })
    }

    // Get assignments where I'm the deputy
    const asDeputy = db.prepare(`
      SELECT 
        da.id, da.principal_id as principalId, da.deputy_id as deputyId,
        da.unit_id as unitId, da.valid_from as validFrom, da.valid_until as validUntil,
        da.reason, da.can_delegate as canDelegate,
        p.first_name || ' ' || p.last_name as principalName,
        ou.name as unitName
      FROM deputy_assignments da
      JOIN employees p ON p.id = da.principal_id
      LEFT JOIN organizational_units ou ON ou.id = da.unit_id
      WHERE da.deputy_id = ?
        AND da.valid_from <= datetime('now')
        AND da.valid_until >= datetime('now')
      ORDER BY da.valid_from DESC
    `).all(req.user.employeeId)

    // Get assignments where I'm the principal
    const asPrincipal = db.prepare(`
      SELECT 
        da.id, da.principal_id as principalId, da.deputy_id as deputyId,
        da.unit_id as unitId, da.valid_from as validFrom, da.valid_until as validUntil,
        da.reason,
        d.first_name || ' ' || d.last_name as deputyName,
        ou.name as unitName
      FROM deputy_assignments da
      JOIN employees d ON d.id = da.deputy_id
      LEFT JOIN organizational_units ou ON ou.id = da.unit_id
      WHERE da.principal_id = ?
        AND da.valid_from <= datetime('now')
        AND da.valid_until >= datetime('now')
      ORDER BY da.valid_from DESC
    `).all(req.user.employeeId)

    res.json({ success: true, data: { asDeputy, asPrincipal } })
  } catch (error) {
    logger.error('Error fetching deputy assignments:', error)
    next(error)
  }
})

// Create deputy assignment
router.post('/deputies',
  authenticate,
  [
    body('deputyId').isUUID(),
    body('validFrom').isISO8601(),
    body('validUntil').isISO8601()
  ],
  async (req: AuthRequest, res: any, next: any) => {
    try {
      const errors = validationResult(req)
      if (!errors.isEmpty()) {
        return res.status(400).json({ success: false, error: { message: 'Invalid input', details: errors.array() } })
      }

      if (!req.user?.employeeId) {
        return res.status(403).json({ success: false, error: { message: 'No employee linked to user' } })
      }

      const { deputyId, unitId, validFrom, validUntil, reason, canDelegate } = req.body
      const now = new Date().toISOString()
      const assignmentId = uuidv4()

      // Check for conflicts
      const conflict = db.prepare(`
        SELECT id FROM deputy_assignments
        WHERE principal_id = ? AND deputy_id = ?
          AND ((valid_from BETWEEN ? AND ?) OR (valid_until BETWEEN ? AND ?))
      `).get(req.user.employeeId, deputyId, validFrom, validUntil, validFrom, validUntil)

      if (conflict) {
        return res.status(400).json({ success: false, error: { message: 'Conflicting assignment exists' } })
      }

      db.prepare(`
        INSERT INTO deputy_assignments (
          id, principal_id, deputy_id, unit_id,
          valid_from, valid_until, reason, can_delegate,
          created_by, created_at, updated_at
        ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
      `).run(
        assignmentId, req.user.employeeId, deputyId, unitId || null,
        validFrom, validUntil, reason || null, canDelegate ? 1 : 0,
        req.user.id, now, now
      )

      res.json({ success: true, data: { id: assignmentId } })
    } catch (error) {
      logger.error('Error creating deputy assignment:', error)
      next(error)
    }
  }
)

// Alias: Create deputy assignment for current user (same as above, convenient endpoint)
router.post('/deputies/my',
  authenticate,
  [
    body('deputyId').isUUID(),
    body('validFrom').isISO8601(),
    body('validUntil').optional({ nullable: true }).isISO8601(),
    body('unitId').optional({ nullable: true }).isUUID()
  ],
  async (req: AuthRequest, res: any, next: any) => {
    try {
      const errors = validationResult(req)
      if (!errors.isEmpty()) {
        return res.status(400).json({ success: false, error: { message: 'Invalid input', details: errors.array() } })
      }

      if (!req.user?.employeeId) {
        return res.status(403).json({ success: false, error: { message: 'No employee linked to user' } })
      }

      const { deputyId, unitId, validFrom, validUntil, reason, canDelegate } = req.body
      const now = new Date().toISOString()
      const assignmentId = uuidv4()

      // Check for conflicts
      const conflict = db.prepare(`
        SELECT id FROM deputy_assignments
        WHERE principal_id = ? AND deputy_id = ?
          AND ((valid_from BETWEEN ? AND ?) OR (valid_until BETWEEN ? AND ?))
      `).get(req.user.employeeId, deputyId, validFrom, validUntil || validFrom, validFrom, validUntil || validFrom)

      if (conflict) {
        return res.status(400).json({ success: false, error: { message: 'Conflicting assignment exists' } })
      }

      db.prepare(`
        INSERT INTO deputy_assignments (
          id, principal_id, deputy_id, unit_id,
          valid_from, valid_until, reason, can_delegate,
          created_by, created_at, updated_at
        ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
      `).run(
        assignmentId, req.user.employeeId, deputyId, unitId || null,
        validFrom, validUntil || validFrom, reason || null, canDelegate ? 1 : 0,
        req.user.id, now, now
      )

      res.json({ success: true, data: { id: assignmentId } })
    } catch (error) {
      logger.error('Error creating deputy assignment (my):', error)
      next(error)
    }
  }
)

// Delegate deputy assignment
router.post('/deputies/delegate',
  authenticate,
  [
    body('assignmentId').isUUID(),
    body('toDeputyId').isUUID(),
    body('reason').optional().trim()
  ],
  async (req: AuthRequest, res: any, next: any) => {
    try {
      const errors = validationResult(req)
      if (!errors.isEmpty()) {
        return res.status(400).json({ success: false, error: { message: 'Invalid input', details: errors.array() } })
      }

      if (!req.user?.employeeId) {
        return res.status(403).json({ success: false, error: { message: 'No employee linked to user' } })
      }

      const { assignmentId, toDeputyId, reason } = req.body
      const now = new Date().toISOString()
      const delegationId = uuidv4()

      // Check if user can delegate this assignment
      const assignment = db.prepare(`
        SELECT * FROM deputy_assignments
        WHERE id = ? AND deputy_id = ? AND can_delegate = 1
          AND valid_from <= datetime('now')
          AND valid_until >= datetime('now')
      `).get(assignmentId, req.user.employeeId) as any

      if (!assignment) {
        return res.status(403).json({ success: false, error: { message: 'Cannot delegate this assignment' } })
      }

      db.prepare(`
        INSERT INTO deputy_delegations (
          id, original_assignment_id, from_deputy_id, to_deputy_id,
          reason, delegated_at, created_at
        ) VALUES (?, ?, ?, ?, ?, ?, ?)
      `).run(
        delegationId, assignmentId, req.user.employeeId, toDeputyId,
        reason || null, now, now
      )

      res.json({ success: true, data: { id: delegationId } })
    } catch (error) {
      logger.error('Error delegating deputy assignment:', error)
      next(error)
    }
  }
)

// Delete deputy assignment (principal or admin)
router.delete('/deputies/:id', authenticate, async (req: AuthRequest, res: any, next: any) => {
  try {
    const { id } = req.params
    if (!id) {
      return res.status(400).json({ success: false, error: { message: 'Missing id' } })
    }

    // Load assignment
    const assignment = db.prepare(`
      SELECT id, principal_id as principalId FROM deputy_assignments WHERE id = ?
    `).get(id) as any

    if (!assignment) {
      return res.status(404).json({ success: false, error: { message: 'Assignment not found' } })
    }

    // Permission: principal or admin/superuser
    const isAdmin = req.user?.role === 'admin' || req.user?.role === 'superuser'
    if (!isAdmin && req.user?.employeeId !== assignment.principalId) {
      return res.status(403).json({ success: false, error: { message: 'Access denied' } })
    }

    // Delete delegations first (FK not ON DELETE CASCADE)
    db.prepare('DELETE FROM deputy_delegations WHERE original_assignment_id = ?').run(id)
    // Delete assignment
    db.prepare('DELETE FROM deputy_assignments WHERE id = ?').run(id)

    return res.json({ success: true })
  } catch (error) {
    logger.error('Error deleting deputy assignment:', error)
    next(error)
  }
})

// Get deputy chain for an assignment
router.get('/deputies/chain/:id', authenticate, async (req: AuthRequest, res, next) => {
  try {
    const chain = []
    
    // Get original assignment
    const assignment = db.prepare(`
      SELECT 
        da.*, 
        p.first_name || ' ' || p.last_name as principalName,
        d.first_name || ' ' || d.last_name as deputyName
      FROM deputy_assignments da
      JOIN employees p ON p.id = da.principal_id
      JOIN employees d ON d.id = da.deputy_id
      WHERE da.id = ?
    `).get(req.params.id) as any

    if (!assignment) {
      return res.status(404).json({ success: false, error: { message: 'Assignment not found' } })
    }

    chain.push({
      type: 'original',
      from: assignment.principalName,
      to: assignment.deputyName,
      reason: assignment.reason
    })

    // Get all delegations
    const delegations = db.prepare(`
      SELECT 
        dd.*,
        f.first_name || ' ' || f.last_name as fromName,
        t.first_name || ' ' || t.last_name as toName
      FROM deputy_delegations dd
      JOIN employees f ON f.id = dd.from_deputy_id
      JOIN employees t ON t.id = dd.to_deputy_id
      WHERE dd.original_assignment_id = ?
      ORDER BY dd.delegated_at
    `).all(req.params.id) as any[]

    delegations.forEach(del => {
      chain.push({
        type: 'delegation',
        from: del.fromName,
        to: del.toName,
        reason: del.reason,
        delegatedAt: del.delegated_at
      })
    })

    res.json({ success: true, data: chain })
  } catch (error) {
    logger.error('Error fetching deputy chain:', error)
    next(error)
  }
})

// Get special positions
router.get('/special-positions', authenticate, async (req: AuthRequest, res, next) => {
  try {
    const positions = db.prepare(`
      SELECT 
        sp.id, sp.position_type as positionType,
        sp.employee_id as employeeId,
        sp.unit_id as unitId,
        sp.start_date as startDate,
        sp.end_date as endDate,
        e.first_name || ' ' || e.last_name as employeeName,
        e.photo,
        ou.name as unitName
      FROM special_positions sp
      JOIN employees e ON e.id = sp.employee_id
      LEFT JOIN organizational_units ou ON ou.id = sp.unit_id
      WHERE sp.is_active = 1
        AND (sp.end_date IS NULL OR sp.end_date > datetime('now'))
      ORDER BY sp.position_type, e.last_name
    `).all()

    res.json({ success: true, data: positions })
  } catch (error) {
    logger.error('Error fetching special positions:', error)
    next(error)
  }
})

export default router
