import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:provider/provider.dart'; import '../providers/pet_provider.dart'; import '../services/api_service.dart'; import '../theme/app_colors.dart'; import '../widgets/common/custom_app_bar.dart'; import '../widgets/common/custom_time_picker.dart'; class ScheduleFormScreen extends StatefulWidget { final DateTime selectedDate; const ScheduleFormScreen({super.key, required this.selectedDate}); @override State createState() => _ScheduleFormScreenState(); } class _ScheduleFormScreenState extends State { final TextEditingController _titleController = TextEditingController(); final TextEditingController _noteController = TextEditingController(); // 시간 설정 상태 late TimeOfDay _startTime; late TimeOfDay _endTime; bool _isEndTimeManuallySet = false; // 사용자가 종료시간을 직접 수정했는지 여부 String? _activePicker; // 'start', 'end', or null String _selectedType = 'general'; // 'general' or 'important' bool _isSubstituting = false; // "저장 중" 상태 표시 // 반복 설정 상태 bool _isRepeatExpanded = false; final TextEditingController _repeatIntervalController = TextEditingController( text: '1', ); String? _repeatUnit; // null, 'day', 'week', 'month', 'year' // 알림 설정 상태 bool _isAlarmExpanded = false; String _selectedAlarmType = 'none'; // 'none', 'start', '10min', '1hour', '1day', 'custom' String _customAlarmUnit = 'min'; // 'min', 'hour', 'day', 'week' final TextEditingController _customAlarmController = TextEditingController(); @override void initState() { super.initState(); // 초기 시간 설정: 시작 08:00, 종료 09:00 _startTime = const TimeOfDay(hour: 8, minute: 0); _endTime = const TimeOfDay(hour: 9, minute: 0); } @override void dispose() { _titleController.dispose(); _noteController.dispose(); _repeatIntervalController.dispose(); _customAlarmController.dispose(); super.dispose(); } // 시작 시간 변경 핸들러 void _onStartTimeChanged(TimeOfDay newTime) { setState(() { _startTime = newTime; // 종료 시간을 수동으로 설정하지 않았다면, 자동으로 +1시간 if (!_isEndTimeManuallySet) { int endHour = newTime.hour + 1; int endMinute = newTime.minute; // 24시 넘어가면 0시로 (단순화) if (endHour >= 24) { endHour -= 24; } _endTime = TimeOfDay(hour: endHour, minute: endMinute); } }); } // 종료 시간 변경 핸들러 void _onEndTimeChanged(TimeOfDay newTime) { setState(() { _endTime = newTime; _isEndTimeManuallySet = true; // 수동 설정됨 -> 자동 연동 해제 }); } String _formatTime(TimeOfDay time) { final period = time.period == DayPeriod.am ? '오전' : '오후'; final hour = time.hourOfPeriod == 0 ? 12 : time.hourOfPeriod; final minute = time.minute.toString().padLeft(2, '0'); return '$period $hour:$minute'; } String _getRepeatText() { if (_repeatUnit == null) return '반복 안 함'; final interval = _repeatIntervalController.text; String unitText = ''; switch (_repeatUnit) { case 'day': unitText = '일'; break; case 'week': unitText = '주'; break; case 'month': unitText = '개월'; break; case 'year': unitText = '년'; break; } return '$interval$unitText마다'; } Future _saveSchedule() async { final title = _titleController.text.trim(); if (title.isEmpty) return; final petProvider = context.read(); final selectedPet = petProvider.selectedPet; if (selectedPet == null) { ScaffoldMessenger.of( context, ).showSnackBar(const SnackBar(content: Text('반려동물을 먼저 선택해주세요.'))); return; } setState(() { _isSubstituting = true; }); try { final apiService = ApiService(); // 시작/종료 시간 DateTime 생성 final startDateTime = DateTime( widget.selectedDate.year, widget.selectedDate.month, widget.selectedDate.day, _startTime.hour, _startTime.minute, ); // 반복 정보 int? repeatInterval; if (_repeatUnit != null) { repeatInterval = int.tryParse(_repeatIntervalController.text) ?? 1; } // 알림 시간 계산 DateTime? alarmTime; bool isAlarmOn = _selectedAlarmType != 'none'; if (isAlarmOn) { switch (_selectedAlarmType) { case 'start': alarmTime = startDateTime; break; case '10min': alarmTime = startDateTime.subtract(const Duration(minutes: 10)); break; case '1hour': alarmTime = startDateTime.subtract(const Duration(hours: 1)); break; case '1day': alarmTime = startDateTime.subtract(const Duration(days: 1)); break; case 'custom': final value = int.tryParse(_customAlarmController.text) ?? 0; if (value > 0) { Duration duration; switch (_customAlarmUnit) { case 'min': duration = Duration(minutes: value); break; case 'hour': duration = Duration(hours: value); break; case 'day': duration = Duration(days: value); break; case 'week': duration = Duration(days: value * 7); break; default: duration = Duration(minutes: value); } alarmTime = startDateTime.subtract(duration); } else { alarmTime = startDateTime; } break; } } final data = { 'petId': selectedPet.id, 'date': startDateTime.toIso8601String(), 'type': _selectedType, 'isCompleted': false, 'title': title, 'note': _noteController.text.trim().isEmpty ? null : _noteController.text.trim(), 'repeatInterval': repeatInterval, 'repeatUnit': _repeatUnit, 'isAlarmOn': isAlarmOn, 'alarmTime': alarmTime?.toIso8601String(), }; await apiService.createSchedule(data); if (mounted) { context .read() .refreshPets(); // Optional: Refresh if needed Navigator.pop(context, true); // 성공 시 true 반환 } } catch (e) { debugPrint('Error saving schedule: $e'); if (mounted) { ScaffoldMessenger.of( context, ).showSnackBar(SnackBar(content: Text('일정 저장 실패: $e'))); } } finally { if (mounted) { setState(() { _isSubstituting = false; }); } } } @override Widget build(BuildContext context) { return Scaffold( backgroundColor: Colors.white, appBar: const CustomAppBar(title: '일정 추가'), body: SafeArea( child: Column( children: [ Expanded( child: SingleChildScrollView( padding: EdgeInsets.symmetric(horizontal: 20.w, vertical: 20.h), child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ // 제목 입력 TextField( onTap: () { setState(() { _activePicker = null; _isRepeatExpanded = false; _isAlarmExpanded = false; }); }, controller: _titleController, decoration: InputDecoration( labelText: '일정 제목', floatingLabelStyle: const TextStyle( color: AppColors.highlight, ), hintText: '예: 산책하기, 병원 방문', border: OutlineInputBorder( borderRadius: BorderRadius.circular(12.r), ), enabledBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(12.r), borderSide: BorderSide(color: Colors.grey[300]!), ), focusedBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(12.r), borderSide: const BorderSide( color: AppColors.highlight, ), ), filled: false, ), ), SizedBox(height: 24.h), // 시작 시간 / 종료 시간 (좌우 배치) Row( children: [ // 시작 시간 Expanded( child: _buildTimeField( label: '시작', type: 'start', time: _startTime, ), ), SizedBox(width: 12.w), // 종료 시간 Expanded( child: _buildTimeField( label: '종료', type: 'end', time: _endTime, ), ), ], ), // 피커 영역 (아코디언) AnimatedSize( duration: const Duration(milliseconds: 300), curve: Curves.easeInOut, child: Container( // 피커가 열렸을 때만 공간 차지 height: _activePicker != null ? null : 0, margin: _activePicker != null ? EdgeInsets.symmetric(vertical: 10.h) : EdgeInsets.zero, decoration: BoxDecoration( color: Colors.transparent, borderRadius: BorderRadius.circular(12.r), border: Border.all(color: Colors.grey[200]!), ), child: _activePicker != null ? CustomTimePicker( initialTime: _activePicker == 'start' ? _startTime : _endTime, onTimeChanged: (newTime) { if (_activePicker == 'start') { _onStartTimeChanged(newTime); } else { _onEndTimeChanged(newTime); } }, ) : const SizedBox.shrink(), ), ), SizedBox(height: _activePicker != null ? 0 : 24.h), // 중요도 선택 Row( children: [ Expanded( child: _buildTypeButton( '일반', 'general', AppColors.subHighlight, ), ), SizedBox(width: 12.w), Expanded( child: _buildTypeButton( '중요', 'important', AppColors.highlight, ), ), ], ), SizedBox(height: 16.h), // 반복 설정 _buildRepeatSection(), SizedBox(height: 16.h), // 알림 설정 _buildAlarmSection(), SizedBox(height: 16.h), // 메모 입력 TextField( onTap: () { setState(() { _activePicker = null; _isRepeatExpanded = false; _isAlarmExpanded = false; }); }, controller: _noteController, maxLines: 3, decoration: InputDecoration( labelText: '메모 (선택)', floatingLabelStyle: const TextStyle( color: AppColors.highlight, ), hintText: '추가적인 내용을 입력하세요', border: OutlineInputBorder( borderRadius: BorderRadius.circular(12.r), ), enabledBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(12.r), borderSide: BorderSide(color: Colors.grey[300]!), ), focusedBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(12.r), borderSide: const BorderSide( color: AppColors.highlight, ), ), filled: false, ), ), SizedBox(height: 24.h), ], ), ), ), ], ), ), bottomNavigationBar: SafeArea( child: Padding( padding: EdgeInsets.all(20.w), child: ElevatedButton( onPressed: _isSubstituting ? null : _saveSchedule, style: ElevatedButton.styleFrom( backgroundColor: AppColors.highlight, padding: EdgeInsets.symmetric(vertical: 16.h), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12.r), ), elevation: 0, ), child: _isSubstituting ? SizedBox( width: 24.w, height: 24.w, child: const CircularProgressIndicator( color: Colors.white, strokeWidth: 2, ), ) : Text( '저장하기', style: TextStyle( fontFamily: 'SCDream', fontSize: 16.sp, fontWeight: FontWeight.bold, color: Colors.white, ), ), ), ), ), ); } String _getAlarmText() { switch (_selectedAlarmType) { case 'none': return '사용 안함'; case 'start': return '일정 시작시간'; case '10min': return '10분 전'; case '1hour': return '1시간 전'; case '1day': return '1일 전'; case 'custom': final value = _customAlarmController.text; if (value.isEmpty) return '직접 입력'; String unitText = ''; switch (_customAlarmUnit) { case 'min': unitText = '분 전'; break; case 'hour': unitText = '시간 전'; break; case 'day': unitText = '일 전'; break; case 'week': unitText = '주 전'; break; } return '$value$unitText'; default: return '사용 안함'; } } Widget _buildAlarmSection() { final borderColor = _isAlarmExpanded ? AppColors.highlight : Colors.grey[300]!; return Container( decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(12.r), border: Border.all(color: borderColor, width: _isAlarmExpanded ? 2 : 1), ), child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ InkWell( onTap: () { setState(() { _isAlarmExpanded = !_isAlarmExpanded; if (_isAlarmExpanded) { _isRepeatExpanded = false; // 다른 섹션 닫기 _activePicker = null; // 시간 설정 닫기 } }); }, borderRadius: BorderRadius.only( topLeft: Radius.circular(12.r), topRight: Radius.circular(12.r), bottomLeft: Radius.circular(_isAlarmExpanded ? 0 : 12.r), bottomRight: Radius.circular(_isAlarmExpanded ? 0 : 12.r), ), child: Padding( padding: EdgeInsets.symmetric(vertical: 16.h, horizontal: 16.w), child: Row( children: [ Text( '알림', style: TextStyle( fontFamily: 'SCDream', fontSize: 12.sp, color: Colors.grey[600], ), ), SizedBox(width: 8.w), Expanded( child: Text( _getAlarmText(), textAlign: TextAlign.right, style: TextStyle( fontFamily: 'SCDream', fontSize: 14.sp, fontWeight: FontWeight.bold, color: _selectedAlarmType != 'none' ? AppColors.highlight : Colors.black, ), ), ), SizedBox(width: 8.w), Icon( _isAlarmExpanded ? Icons.keyboard_arrow_up : Icons.keyboard_arrow_down, color: Colors.grey[400], size: 20.w, ), ], ), ), ), AnimatedSize( duration: const Duration(milliseconds: 300), curve: Curves.easeInOut, child: Container( clipBehavior: Clip.hardEdge, decoration: const BoxDecoration(), height: _isAlarmExpanded ? null : 0, padding: EdgeInsets.fromLTRB(16.w, 0, 16.w, 16.h), child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ // Row 1: 사용안함, 시작시간 (위치 변경됨) Row( children: [ Expanded(child: _buildAlarmOptionButton('사용 안함', 'none')), SizedBox(width: 8.w), Expanded( child: _buildAlarmOptionButton('일정 시작시간', 'start'), ), ], ), SizedBox(height: 8.h), // Row 2: 10분, 1시간, 1일 Row( children: [ Expanded( child: _buildAlarmOptionButton('10분 전', '10min'), ), SizedBox(width: 8.w), Expanded( child: _buildAlarmOptionButton('1시간 전', '1hour'), ), SizedBox(width: 8.w), Expanded(child: _buildAlarmOptionButton('1일 전', '1day')), ], ), SizedBox(height: 8.h), // Row 3: 직접입력 버튼 + 입력칸 Row( children: [ Expanded( child: _buildAlarmOptionButton('직접 입력', 'custom'), ), SizedBox(width: 8.w), Expanded( child: Container( height: 34.h, // 버튼 높이와 비슷하게 조정 (48.h -> 34.h) decoration: BoxDecoration( border: Border.all(color: Colors.grey[300]!), borderRadius: BorderRadius.circular(20.r), color: _selectedAlarmType == 'custom' ? Colors.white : Colors.grey[100], ), alignment: Alignment.center, child: TextField( controller: _customAlarmController, keyboardType: TextInputType.number, textAlign: TextAlign.center, onTap: () { setState(() { _selectedAlarmType = 'custom'; }); }, style: TextStyle( fontFamily: 'SCDream', fontSize: 16.sp, fontWeight: _selectedAlarmType == 'custom' ? FontWeight.bold : FontWeight.normal, color: _selectedAlarmType == 'custom' ? Colors.black : Colors.grey[400], height: 1.0, ), decoration: InputDecoration( border: InputBorder.none, isCollapsed: true, contentPadding: EdgeInsets.zero, hintText: '0', hintStyle: TextStyle( color: Colors.grey[400], fontSize: 16.sp, ), ), ), ), ), ], ), // Row 4: 단위 버튼 (custom일 때만 표시) if (_selectedAlarmType == 'custom') ...[ SizedBox(height: 12.h), Row( children: [ Expanded(child: _buildCustomUnitButton('분 전', 'min')), SizedBox(width: 8.w), Expanded(child: _buildCustomUnitButton('시간 전', 'hour')), SizedBox(width: 8.w), Expanded(child: _buildCustomUnitButton('일 전', 'day')), SizedBox(width: 8.w), Expanded(child: _buildCustomUnitButton('주 전', 'week')), ], ), ], ], ), ), ), ], ), ); } Widget _buildCustomUnitButton(String label, String unit) { final isSelected = _customAlarmUnit == unit; return InkWell( onTap: () { setState(() { _customAlarmUnit = unit; }); }, borderRadius: BorderRadius.circular(20.r), child: Container( width: double.infinity, alignment: Alignment.center, padding: EdgeInsets.symmetric(vertical: 8.h), decoration: BoxDecoration( color: isSelected ? AppColors.highlight : Colors.grey[100], borderRadius: BorderRadius.circular(20.r), ), child: Text( label, style: TextStyle( fontFamily: 'SCDream', fontSize: 13.sp, color: isSelected ? Colors.white : Colors.grey[600], fontWeight: isSelected ? FontWeight.bold : FontWeight.normal, ), ), ), ); } Widget _buildAlarmOptionButton(String label, String value) { final isSelected = _selectedAlarmType == value; return InkWell( onTap: () { setState(() { _selectedAlarmType = value; if (value == 'custom') { // Focus custom input? } }); }, borderRadius: BorderRadius.circular(20.r), child: Container( width: double.infinity, alignment: Alignment.center, padding: EdgeInsets.symmetric(vertical: 8.h), decoration: BoxDecoration( color: isSelected ? AppColors.highlight : Colors.grey[100], borderRadius: BorderRadius.circular(20.r), ), child: Text( label, style: TextStyle( fontFamily: 'SCDream', fontSize: 13.sp, color: isSelected ? Colors.white : Colors.grey[600], fontWeight: isSelected ? FontWeight.bold : FontWeight.normal, ), ), ), ); } Widget _buildRepeatSection() { final borderColor = _isRepeatExpanded ? AppColors.highlight : Colors.grey[300]!; return Container( decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(12.r), border: Border.all( color: borderColor, width: _isRepeatExpanded ? 2 : 1, ), ), child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ InkWell( onTap: () { setState(() { _isRepeatExpanded = !_isRepeatExpanded; if (_isRepeatExpanded) { _isAlarmExpanded = false; // 다른 섹션 닫기 _activePicker = null; // 시간 설정 닫기 } }); }, borderRadius: BorderRadius.only( topLeft: Radius.circular(12.r), topRight: Radius.circular(12.r), bottomLeft: Radius.circular(_isRepeatExpanded ? 0 : 12.r), bottomRight: Radius.circular(_isRepeatExpanded ? 0 : 12.r), ), child: Padding( padding: EdgeInsets.symmetric(vertical: 16.h, horizontal: 16.w), child: Row( children: [ Text( '반복', style: TextStyle( fontFamily: 'SCDream', fontSize: 12.sp, color: Colors.grey[600], ), ), SizedBox(width: 8.w), Expanded( child: Text( _getRepeatText(), textAlign: TextAlign.right, style: TextStyle( fontFamily: 'SCDream', fontSize: 14.sp, fontWeight: FontWeight.bold, color: _repeatUnit != null ? AppColors.highlight : Colors.black, ), ), ), SizedBox(width: 8.w), Icon( _isRepeatExpanded ? Icons.keyboard_arrow_up : Icons.keyboard_arrow_down, color: Colors.grey[400], size: 20.w, ), ], ), ), ), AnimatedSize( duration: const Duration(milliseconds: 300), curve: Curves.easeInOut, child: Container( clipBehavior: Clip.hardEdge, decoration: const BoxDecoration(), height: _isRepeatExpanded ? null : 0, padding: EdgeInsets.fromLTRB(16.w, 0, 16.w, 16.h), child: Column( children: [ Row( children: [ Container( width: 60.w, height: 40.h, margin: EdgeInsets.only(right: 12.w), decoration: BoxDecoration( border: Border.all(color: Colors.grey[300]!), borderRadius: BorderRadius.circular(8.r), ), alignment: Alignment.center, child: TextField( controller: _repeatIntervalController, keyboardType: TextInputType.number, textAlign: TextAlign.center, style: TextStyle( fontFamily: 'SCDream', fontSize: 16.sp, fontWeight: FontWeight.bold, height: 1.0, ), decoration: const InputDecoration( border: InputBorder.none, isCollapsed: true, contentPadding: EdgeInsets.zero, ), onChanged: (value) { if (value == '0') { _repeatIntervalController.text = '1'; } setState(() {}); }, ), ), // 단위 선택 버튼들 Expanded( child: SingleChildScrollView( scrollDirection: Axis.horizontal, child: Row( mainAxisAlignment: MainAxisAlignment.start, children: [ _buildRepeatUnitButton('일', 'day'), SizedBox(width: 8.w), _buildRepeatUnitButton('주', 'week'), SizedBox(width: 8.w), _buildRepeatUnitButton('개월', 'month'), SizedBox(width: 8.w), _buildRepeatUnitButton('년', 'year'), ], ), ), ), ], ), SizedBox(height: 12.h), // 반복 안 함 버튼 (하단 배치) InkWell( onTap: () { setState(() { _repeatUnit = null; }); }, borderRadius: BorderRadius.circular(8.r), child: Container( width: double.infinity, padding: EdgeInsets.symmetric(vertical: 10.h), decoration: BoxDecoration( color: _repeatUnit == null ? AppColors.highlight : Colors.grey[100], borderRadius: BorderRadius.circular(8.r), ), alignment: Alignment.center, child: Text( '반복 안 함', style: TextStyle( fontFamily: 'SCDream', fontSize: 14.sp, color: _repeatUnit == null ? Colors.white : Colors.grey[600], fontWeight: _repeatUnit == null ? FontWeight.bold : FontWeight.normal, ), ), ), ), ], ), ), ), ], ), ); } Widget _buildRepeatUnitButton(String label, String? unit) { final isSelected = _repeatUnit == unit; return InkWell( onTap: () { setState(() { _repeatUnit = unit; }); }, borderRadius: BorderRadius.circular(20.r), child: Container( padding: EdgeInsets.symmetric(horizontal: 16.w, vertical: 8.h), decoration: BoxDecoration( color: isSelected ? AppColors.highlight : Colors.grey[100], borderRadius: BorderRadius.circular(20.r), ), child: Text( label, style: TextStyle( fontFamily: 'SCDream', fontSize: 14.sp, color: isSelected ? Colors.white : Colors.grey[600], fontWeight: isSelected ? FontWeight.bold : FontWeight.normal, ), ), ), ); } Widget _buildTimeField({ required String label, required String type, required TimeOfDay time, }) { final isActive = _activePicker == type; final borderColor = isActive ? AppColors.highlight : Colors.grey[300]!; final textColor = isActive ? AppColors.highlight : Colors.black; return GestureDetector( onTap: () { setState(() { if (_activePicker == type) { _activePicker = null; // 이미 열려있으면 닫기 } else { _activePicker = type; // 열기 _isRepeatExpanded = false; // 다른 섹션 닫기 _isAlarmExpanded = false; // 다른 섹션 닫기 } }); }, child: Container( padding: EdgeInsets.symmetric(vertical: 16.h, horizontal: 16.w), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(12.r), border: Border.all(color: borderColor, width: isActive ? 2 : 1), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( label, style: TextStyle( fontFamily: 'SCDream', fontSize: 12.sp, color: Colors.grey[600], ), ), SizedBox(height: 4.h), Text( _formatTime(time), style: TextStyle( fontFamily: 'SCDream', fontSize: 18.sp, fontWeight: FontWeight.bold, color: textColor, ), ), ], ), ), ); } Widget _buildTypeButton(String label, String value, Color color) { final isSelected = _selectedType == value; return InkWell( onTap: () { setState(() { _selectedType = value; _activePicker = null; _isRepeatExpanded = false; _isAlarmExpanded = false; }); }, borderRadius: BorderRadius.circular(12.r), child: Container( padding: EdgeInsets.symmetric(vertical: 12.h), decoration: BoxDecoration( color: isSelected ? color.withOpacity(0.1) : Colors.transparent, border: Border.all( color: isSelected ? color : Colors.grey[300]!, width: isSelected ? 2 : 1, ), borderRadius: BorderRadius.circular(12.r), ), child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ Container( width: 12.w, height: 12.w, decoration: BoxDecoration(color: color, shape: BoxShape.circle), ), SizedBox(width: 8.w), Text( label, style: TextStyle( fontFamily: 'SCDream', fontSize: 14.sp, fontWeight: isSelected ? FontWeight.bold : FontWeight.normal, color: isSelected ? color : Colors.grey[600], ), ), ], ), ), ); } }